]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/AjaxResponse.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / includes / AjaxResponse.php
1 <?php
2 /**
3  * Response handler for Ajax requests.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup Ajax
22  */
23 use MediaWiki\MediaWikiServices;
24
25 /**
26  * Handle responses for Ajax requests (send headers, print
27  * content, that sort of thing)
28  *
29  * @ingroup Ajax
30  */
31 class AjaxResponse {
32         /**
33          * Number of seconds to get the response cached by a proxy
34          * @var int $mCacheDuration
35          */
36         private $mCacheDuration;
37
38         /**
39          * HTTP header Content-Type
40          * @var string $mContentType
41          */
42         private $mContentType;
43
44         /**
45          * Disables output. Can be set by calling $AjaxResponse->disable()
46          * @var bool $mDisabled
47          */
48         private $mDisabled;
49
50         /**
51          * Date for the HTTP header Last-modified
52          * @var string|bool $mLastModified
53          */
54         private $mLastModified;
55
56         /**
57          * HTTP response code
58          * @var string $mResponseCode
59          */
60         private $mResponseCode;
61
62         /**
63          * HTTP Vary header
64          * @var string $mVary
65          */
66         private $mVary;
67
68         /**
69          * Content of our HTTP response
70          * @var string $mText
71          */
72         private $mText;
73
74         /**
75          * @var Config
76          */
77         private $mConfig;
78
79         /**
80          * @param string|null $text
81          * @param Config|null $config
82          */
83         function __construct( $text = null, Config $config = null ) {
84                 $this->mCacheDuration = null;
85                 $this->mVary = null;
86                 $this->mConfig = $config ?: MediaWikiServices::getInstance()->getMainConfig();
87
88                 $this->mDisabled = false;
89                 $this->mText = '';
90                 $this->mResponseCode = 200;
91                 $this->mLastModified = false;
92                 $this->mContentType = 'application/x-wiki';
93
94                 if ( $text ) {
95                         $this->addText( $text );
96                 }
97         }
98
99         /**
100          * Set the number of seconds to get the response cached by a proxy
101          * @param int $duration
102          */
103         function setCacheDuration( $duration ) {
104                 $this->mCacheDuration = $duration;
105         }
106
107         /**
108          * Set the HTTP Vary header
109          * @param string $vary
110          */
111         function setVary( $vary ) {
112                 $this->mVary = $vary;
113         }
114
115         /**
116          * Set the HTTP response code
117          * @param string $code
118          */
119         function setResponseCode( $code ) {
120                 $this->mResponseCode = $code;
121         }
122
123         /**
124          * Set the HTTP header Content-Type
125          * @param string $type
126          */
127         function setContentType( $type ) {
128                 $this->mContentType = $type;
129         }
130
131         /**
132          * Disable output.
133          */
134         function disable() {
135                 $this->mDisabled = true;
136         }
137
138         /**
139          * Add content to the response
140          * @param string $text
141          */
142         function addText( $text ) {
143                 if ( !$this->mDisabled && $text ) {
144                         $this->mText .= $text;
145                 }
146         }
147
148         /**
149          * Output text
150          */
151         function printText() {
152                 if ( !$this->mDisabled ) {
153                         print $this->mText;
154                 }
155         }
156
157         /**
158          * Construct the header and output it
159          */
160         function sendHeaders() {
161                 if ( $this->mResponseCode ) {
162                         // For back-compat, it is supported that mResponseCode be a string like " 200 OK"
163                         // (with leading space and the status message after). Cast response code to an integer
164                         // to take advantage of PHP's conversion rules which will turn "  200 OK" into 200.
165                         // https://secure.php.net/manual/en/language.types.string.php#language.types.string.conversion
166                         $n = intval( trim( $this->mResponseCode ) );
167                         HttpStatus::header( $n );
168                 }
169
170                 header( "Content-Type: " . $this->mContentType );
171
172                 if ( $this->mLastModified ) {
173                         header( "Last-Modified: " . $this->mLastModified );
174                 } else {
175                         header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
176                 }
177
178                 if ( $this->mCacheDuration ) {
179                         # If CDN caches are configured, tell them to cache the response,
180                         # and tell the client to always check with the CDN. Otherwise,
181                         # tell the client to use a cached copy, without a way to purge it.
182
183                         if ( $this->mConfig->get( 'UseSquid' ) ) {
184                                 # Expect explicit purge of the proxy cache, but require end user agents
185                                 # to revalidate against the proxy on each visit.
186                                 # Surrogate-Control controls our CDN, Cache-Control downstream caches
187
188                                 if ( $this->mConfig->get( 'UseESI' ) ) {
189                                         header( 'Surrogate-Control: max-age=' . $this->mCacheDuration . ', content="ESI/1.0"' );
190                                         header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
191                                 } else {
192                                         header( 'Cache-Control: s-maxage=' . $this->mCacheDuration . ', must-revalidate, max-age=0' );
193                                 }
194
195                         } else {
196                                 # Let the client do the caching. Cache is not purged.
197                                 header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" );
198                                 header( "Cache-Control: s-maxage={$this->mCacheDuration}," .
199                                         "public,max-age={$this->mCacheDuration}" );
200                         }
201
202                 } else {
203                         # always expired, always modified
204                         header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );    // Date in the past
205                         header( "Cache-Control: no-cache, must-revalidate" );  // HTTP/1.1
206                         header( "Pragma: no-cache" );                          // HTTP/1.0
207                 }
208
209                 if ( $this->mVary ) {
210                         header( "Vary: " . $this->mVary );
211                 }
212         }
213
214         /**
215          * checkLastModified tells the client to use the client-cached response if
216          * possible. If successful, the AjaxResponse is disabled so that
217          * any future call to AjaxResponse::printText() have no effect.
218          *
219          * @param string $timestamp
220          * @return bool Returns true if the response code was set to 304 Not Modified.
221          */
222         function checkLastModified( $timestamp ) {
223                 global $wgCachePages, $wgCacheEpoch, $wgUser;
224                 $fname = 'AjaxResponse::checkLastModified';
225
226                 if ( !$timestamp || $timestamp == '19700101000000' ) {
227                         wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP", 'private' );
228                         return false;
229                 }
230
231                 if ( !$wgCachePages ) {
232                         wfDebug( "$fname: CACHE DISABLED", 'private' );
233                         return false;
234                 }
235
236                 $timestamp = wfTimestamp( TS_MW, $timestamp );
237                 $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->getTouched(), $wgCacheEpoch ) );
238
239                 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
240                         # IE sends sizes after the date like this:
241                         # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
242                         # this breaks strtotime().
243                         $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
244                         $modsinceTime = strtotime( $modsince );
245                         $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
246                         wfDebug( "$fname: -- client send If-Modified-Since: $modsince", 'private' );
247                         wfDebug( "$fname: --  we might send Last-Modified : $lastmod", 'private' );
248
249                         if ( ( $ismodsince >= $timestamp )
250                                 && $wgUser->validateCache( $ismodsince ) &&
251                                 $ismodsince >= $wgCacheEpoch
252                         ) {
253                                 ini_set( 'zlib.output_compression', 0 );
254                                 $this->setResponseCode( 304 );
255                                 $this->disable();
256                                 $this->mLastModified = $lastmod;
257
258                                 wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
259                                         "page: $timestamp ; site $wgCacheEpoch", 'private' );
260
261                                 return true;
262                         } else {
263                                 wfDebug( "$fname: READY  client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
264                                         "page: $timestamp ; site $wgCacheEpoch", 'private' );
265                                 $this->mLastModified = $lastmod;
266                         }
267                 } else {
268                         wfDebug( "$fname: client did not send If-Modified-Since header", 'private' );
269                         $this->mLastModified = $lastmod;
270                 }
271                 return false;
272         }
273
274         /**
275          * @param string $mckey
276          * @param int $touched
277          * @return bool
278          */
279         function loadFromMemcached( $mckey, $touched ) {
280                 if ( !$touched ) {
281                         return false;
282                 }
283
284                 $mcvalue = ObjectCache::getMainWANInstance()->get( $mckey );
285                 if ( $mcvalue ) {
286                         # Check to see if the value has been invalidated
287                         if ( $touched <= $mcvalue['timestamp'] ) {
288                                 wfDebug( "Got $mckey from cache" );
289                                 $this->mText = $mcvalue['value'];
290
291                                 return true;
292                         } else {
293                                 wfDebug( "$mckey has expired" );
294                         }
295                 }
296
297                 return false;
298         }
299
300         /**
301          * @param string $mckey
302          * @param int $expiry
303          * @return bool
304          */
305         function storeInMemcached( $mckey, $expiry = 86400 ) {
306                 ObjectCache::getMainWANInstance()->set( $mckey,
307                         [
308                                 'timestamp' => wfTimestampNow(),
309                                 'value' => $this->mText
310                         ], $expiry
311                 );
312
313                 return true;
314         }
315 }