MediaWiki  1.23.14
SquidPurgeClient.php
Go to the documentation of this file.
1 <?php
31  var $host, $port, $ip;
32 
33  var $readState = 'idle';
34  var $writeBuffer = '';
35  var $requests = array();
37 
38  const EINTR = 4;
39  const EAGAIN = 11;
40  const EINPROGRESS = 115;
41  const BUFFER_SIZE = 8192;
42 
46  var $socket;
47 
49 
51 
56  public function __construct( $server, $options = array() ) {
57  $parts = explode( ':', $server, 2 );
58  $this->host = $parts[0];
59  $this->port = isset( $parts[1] ) ? $parts[1] : 80;
60  }
61 
68  protected function getSocket() {
69  if ( $this->socket !== null ) {
70  return $this->socket;
71  }
72 
73  $ip = $this->getIP();
74  if ( !$ip ) {
75  $this->log( "DNS error" );
76  $this->markDown();
77  return false;
78  }
79  $this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
80  socket_set_nonblock( $this->socket );
82  $ok = socket_connect( $this->socket, $ip, $this->port );
84  if ( !$ok ) {
85  $error = socket_last_error( $this->socket );
86  if ( $error !== self::EINPROGRESS ) {
87  $this->log( "connection error: " . socket_strerror( $error ) );
88  $this->markDown();
89  return false;
90  }
91  }
92 
93  return $this->socket;
94  }
95 
100  public function getReadSocketsForSelect() {
101  if ( $this->readState == 'idle' ) {
102  return array();
103  }
104  $socket = $this->getSocket();
105  if ( $socket === false ) {
106  return array();
107  }
108  return array( $socket );
109  }
110 
115  public function getWriteSocketsForSelect() {
116  if ( !strlen( $this->writeBuffer ) ) {
117  return array();
118  }
119  $socket = $this->getSocket();
120  if ( $socket === false ) {
121  return array();
122  }
123  return array( $socket );
124  }
125 
130  protected function getIP() {
131  if ( $this->ip === null ) {
132  if ( IP::isIPv4( $this->host ) ) {
133  $this->ip = $this->host;
134  } elseif ( IP::isIPv6( $this->host ) ) {
135  throw new MWException( '$wgSquidServers does not support IPv6' );
136  } else {
138  $this->ip = gethostbyname( $this->host );
139  if ( $this->ip === $this->host ) {
140  $this->ip = false;
141  }
143  }
144  }
145  return $this->ip;
146  }
147 
152  protected function markDown() {
153  $this->close();
154  $this->socket = false;
155  }
156 
160  public function close() {
161  if ( $this->socket ) {
163  socket_set_block( $this->socket );
164  socket_shutdown( $this->socket );
165  socket_close( $this->socket );
167  }
168  $this->socket = null;
169  $this->readBuffer = '';
170  // Write buffer is kept since it may contain a request for the next socket
171  }
172 
178  public function queuePurge( $url ) {
179  global $wgSquidPurgeUseHostHeader;
180  $url = SquidUpdate::expand( str_replace( "\n", '', $url ) );
181  $request = array();
182  if ( $wgSquidPurgeUseHostHeader ) {
183  $url = wfParseUrl( $url );
184  $host = $url['host'];
185  if ( isset( $url['port'] ) && strlen( $url['port'] ) > 0 ) {
186  $host .= ":" . $url['port'];
187  }
188  $path = $url['path'];
189  if ( isset( $url['query'] ) && is_string( $url['query'] ) ) {
190  $path = wfAppendQuery( $path, $url['query'] );
191  }
192  $request[] = "PURGE $path HTTP/1.1";
193  $request[] = "Host: $host";
194  } else {
195  $request[] = "PURGE $url HTTP/1.0";
196  }
197  $request[] = "Connection: Keep-Alive";
198  $request[] = "Proxy-Connection: Keep-Alive";
199  $request[] = "User-Agent: " . Http::userAgent() . ' ' . __CLASS__;
200  // Two ''s to create \r\n\r\n
201  $request[] = '';
202  $request[] = '';
203 
204  $this->requests[] = implode( "\r\n", $request );
205  if ( $this->currentRequestIndex === null ) {
206  $this->nextRequest();
207  }
208  }
209 
213  public function isIdle() {
214  return strlen( $this->writeBuffer ) == 0 && $this->readState == 'idle';
215  }
216 
220  public function doWrites() {
221  if ( !strlen( $this->writeBuffer ) ) {
222  return;
223  }
224  $socket = $this->getSocket();
225  if ( !$socket ) {
226  return;
227  }
228 
229  if ( strlen( $this->writeBuffer ) <= self::BUFFER_SIZE ) {
230  $buf = $this->writeBuffer;
231  $flags = MSG_EOR;
232  } else {
233  $buf = substr( $this->writeBuffer, 0, self::BUFFER_SIZE );
234  $flags = 0;
235  }
237  $bytesSent = socket_send( $socket, $buf, strlen( $buf ), $flags );
239 
240  if ( $bytesSent === false ) {
241  $error = socket_last_error( $socket );
242  if ( $error != self::EAGAIN && $error != self::EINTR ) {
243  $this->log( 'write error: ' . socket_strerror( $error ) );
244  $this->markDown();
245  }
246  return;
247  }
248 
249  $this->writeBuffer = substr( $this->writeBuffer, $bytesSent );
250  }
251 
255  public function doReads() {
256  $socket = $this->getSocket();
257  if ( !$socket ) {
258  return;
259  }
260 
261  $buf = '';
263  $bytesRead = socket_recv( $socket, $buf, self::BUFFER_SIZE, 0 );
265  if ( $bytesRead === false ) {
266  $error = socket_last_error( $socket );
267  if ( $error != self::EAGAIN && $error != self::EINTR ) {
268  $this->log( 'read error: ' . socket_strerror( $error ) );
269  $this->markDown();
270  return;
271  }
272  } elseif ( $bytesRead === 0 ) {
273  // Assume EOF
274  $this->close();
275  return;
276  }
277 
278  $this->readBuffer .= $buf;
279  while ( $this->socket && $this->processReadBuffer() === 'continue' );
280  }
281 
286  protected function processReadBuffer() {
287  switch ( $this->readState ) {
288  case 'idle':
289  return 'done';
290  case 'status':
291  case 'header':
292  $lines = explode( "\r\n", $this->readBuffer, 2 );
293  if ( count( $lines ) < 2 ) {
294  return 'done';
295  }
296  if ( $this->readState == 'status' ) {
297  $this->processStatusLine( $lines[0] );
298  } else { // header
299  $this->processHeaderLine( $lines[0] );
300  }
301  $this->readBuffer = $lines[1];
302  return 'continue';
303  case 'body':
304  if ( $this->bodyRemaining !== null ) {
305  if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) {
306  $this->bodyRemaining -= strlen( $this->readBuffer );
307  $this->readBuffer = '';
308  return 'done';
309  } else {
310  $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining );
311  $this->bodyRemaining = 0;
312  $this->nextRequest();
313  return 'continue';
314  }
315  } else {
316  // No content length, read all data to EOF
317  $this->readBuffer = '';
318  return 'done';
319  }
320  default:
321  throw new MWException( __METHOD__ . ': unexpected state' );
322  }
323  }
324 
329  protected function processStatusLine( $line ) {
330  if ( !preg_match( '!^HTTP/(\d+)\.(\d+) (\d{3}) (.*)$!', $line, $m ) ) {
331  $this->log( 'invalid status line' );
332  $this->markDown();
333  return;
334  }
335  list( , , , $status, $reason ) = $m;
336  $status = intval( $status );
337  if ( $status !== 200 && $status !== 404 ) {
338  $this->log( "unexpected status code: $status $reason" );
339  $this->markDown();
340  return;
341  }
342  $this->readState = 'header';
343  }
344 
348  protected function processHeaderLine( $line ) {
349  if ( preg_match( '/^Content-Length: (\d+)$/i', $line, $m ) ) {
350  $this->bodyRemaining = intval( $m[1] );
351  } elseif ( $line === '' ) {
352  $this->readState = 'body';
353  }
354  }
355 
356  protected function nextRequest() {
357  if ( $this->currentRequestIndex !== null ) {
358  unset( $this->requests[$this->currentRequestIndex] );
359  }
360  if ( count( $this->requests ) ) {
361  $this->readState = 'status';
362  $this->currentRequestIndex = key( $this->requests );
363  $this->writeBuffer = $this->requests[$this->currentRequestIndex];
364  } else {
365  $this->readState = 'idle';
366  $this->currentRequestIndex = null;
367  $this->writeBuffer = '';
368  }
369  $this->bodyRemaining = null;
370  }
371 
375  protected function log( $msg ) {
376  wfDebugLog( 'squid', __CLASS__ . " ($this->host): $msg" );
377  }
378 }
379 
381 
385  var $clients = array();
386  var $timeout = 5;
387 
391  function __construct( $options = array() ) {
392  if ( isset( $options['timeout'] ) ) {
393  $this->timeout = $options['timeout'];
394  }
395  }
396 
401  public function addClient( $client ) {
402  $this->clients[] = $client;
403  }
404 
405  public function run() {
406  $done = false;
407  $startTime = microtime( true );
408  while ( !$done ) {
409  $readSockets = $writeSockets = array();
413  foreach ( $this->clients as $clientIndex => $client ) {
414  $sockets = $client->getReadSocketsForSelect();
415  foreach ( $sockets as $i => $socket ) {
416  $readSockets["$clientIndex/$i"] = $socket;
417  }
418  $sockets = $client->getWriteSocketsForSelect();
419  foreach ( $sockets as $i => $socket ) {
420  $writeSockets["$clientIndex/$i"] = $socket;
421  }
422  }
423  if ( !count( $readSockets ) && !count( $writeSockets ) ) {
424  break;
425  }
426  $exceptSockets = null;
427  $timeout = min( $startTime + $this->timeout - microtime( true ), 1 );
429  $numReady = socket_select( $readSockets, $writeSockets, $exceptSockets, $timeout );
431  if ( $numReady === false ) {
432  wfDebugLog( 'squid', __METHOD__ . ': Error in stream_select: ' .
433  socket_strerror( socket_last_error() ) . "\n" );
434  break;
435  }
436  // Check for timeout, use 1% tolerance since we aimed at having socket_select()
437  // exit at precisely the overall timeout
438  if ( microtime( true ) - $startTime > $this->timeout * 0.99 ) {
439  wfDebugLog( 'squid', __CLASS__ . ": timeout ({$this->timeout}s)\n" );
440  break;
441  } elseif ( !$numReady ) {
442  continue;
443  }
444 
445  foreach ( $readSockets as $key => $socket ) {
446  list( $clientIndex, ) = explode( '/', $key );
447  $client = $this->clients[$clientIndex];
448  $client->doReads();
449  }
450  foreach ( $writeSockets as $key => $socket ) {
451  list( $clientIndex, ) = explode( '/', $key );
452  $client = $this->clients[$clientIndex];
453  $client->doWrites();
454  }
455 
456  $done = true;
457  foreach ( $this->clients as $client ) {
458  if ( !$client->isIdle() ) {
459  $done = false;
460  }
461  }
462  }
463  foreach ( $this->clients as $client ) {
464  $client->close();
465  }
466  }
467 }
SquidPurgeClient\EINTR
const EINTR
Definition: SquidPurgeClient.php:38
SquidPurgeClient\markDown
markDown()
Close the socket and ignore any future purge requests.
Definition: SquidPurgeClient.php:152
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled also a ContextSource error or success you ll probably need to make sure the header is varied on WebRequest $request
Definition: hooks.txt:1961
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
SquidPurgeClientPool\$timeout
$timeout
Definition: SquidPurgeClient.php:385
SquidPurgeClient\EAGAIN
const EAGAIN
Definition: SquidPurgeClient.php:39
SquidPurgeClientPool\$clients
array $clients
of SquidPurgeClient
Definition: SquidPurgeClient.php:384
SquidPurgeClient\close
close()
Close the socket but allow it to be reopened for future purge requests.
Definition: SquidPurgeClient.php:160
Http\userAgent
static userAgent()
A standard user-agent we can use for external requests.
Definition: HttpFunctions.php:161
SquidPurgeClientPool\__construct
__construct( $options=array())
Definition: SquidPurgeClient.php:390
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1087
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2434
SquidUpdate\expand
static expand( $url)
Expand local URLs to fully-qualified URLs using the internal protocol and host defined in $wgInternal...
Definition: SquidUpdate.php:291
IP\isIPv6
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
Definition: IP.php:85
SquidPurgeClient
An HTTP 1.0 client built for the purposes of purging Squid and Varnish.
Definition: SquidPurgeClient.php:30
SquidPurgeClient\$bodyRemaining
$bodyRemaining
Definition: SquidPurgeClient.php:50
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
SquidPurgeClientPool
Definition: SquidPurgeClient.php:380
SquidPurgeClient\$writeBuffer
$writeBuffer
Definition: SquidPurgeClient.php:34
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:506
key
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database key
Definition: design.txt:25
SquidPurgeClient\processHeaderLine
processHeaderLine( $line)
Definition: SquidPurgeClient.php:348
SquidPurgeClient\getIP
getIP()
Get the host's IP address.
Definition: SquidPurgeClient.php:130
wfParseUrl
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
Definition: GlobalFunctions.php:802
MWException
MediaWiki exception.
Definition: MWException.php:26
SquidPurgeClient\BUFFER_SIZE
const BUFFER_SIZE
Definition: SquidPurgeClient.php:41
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2464
SquidPurgeClient\getSocket
getSocket()
Open a socket if there isn't one open already, return it.
Definition: SquidPurgeClient.php:68
SquidPurgeClient\isIdle
isIdle()
Definition: SquidPurgeClient.php:213
$lines
$lines
Definition: router.php:65
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
SquidPurgeClient\$ip
$ip
Definition: SquidPurgeClient.php:31
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
SquidPurgeClient\$socket
$socket
The socket resource, or null for unconnected, or false for disabled due to error.
Definition: SquidPurgeClient.php:46
SquidPurgeClient\nextRequest
nextRequest()
Definition: SquidPurgeClient.php:356
port
storage can be distributed across multiple and multiple web servers can use the same cache cluster *********************W A R N I N G ***********************Memcached has no security or authentication Please ensure that your server is appropriately and that the port(s) used for memcached servers are not publicly accessible. Otherwise
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
SquidPurgeClient\$currentRequestIndex
$currentRequestIndex
Definition: SquidPurgeClient.php:36
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
$ok
$ok
Definition: UtfNormalTest.php:71
$line
$line
Definition: cdb.php:57
SquidPurgeClient\doReads
doReads()
Read some data.
Definition: SquidPurgeClient.php:255
SquidPurgeClient\$readBuffer
$readBuffer
Definition: SquidPurgeClient.php:48
IP\isIPv4
static isIPv4( $ip)
Given a string, determine if it as valid IP in IPv4 only.
Definition: IP.php:96
SquidPurgeClient\EINPROGRESS
const EINPROGRESS
Definition: SquidPurgeClient.php:40
SquidPurgeClient\doWrites
doWrites()
Perform pending writes.
Definition: SquidPurgeClient.php:220
SquidPurgeClient\$requests
$requests
Definition: SquidPurgeClient.php:35
SquidPurgeClient\getWriteSocketsForSelect
getWriteSocketsForSelect()
Get write socket array for select()
Definition: SquidPurgeClient.php:115
SquidPurgeClient\processStatusLine
processStatusLine( $line)
Definition: SquidPurgeClient.php:329
SquidPurgeClient\queuePurge
queuePurge( $url)
Queue a purge operation.
Definition: SquidPurgeClient.php:178
SquidPurgeClientPool\run
run()
Definition: SquidPurgeClient.php:404
SquidPurgeClient\getReadSocketsForSelect
getReadSocketsForSelect()
Get read socket array for select()
Definition: SquidPurgeClient.php:100
$path
$path
Definition: NoLocalSettings.php:35
SquidPurgeClient\$readState
$readState
Definition: SquidPurgeClient.php:33
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
SquidPurgeClient\processReadBuffer
processReadBuffer()
Definition: SquidPurgeClient.php:286
SquidPurgeClient\$port
$port
Definition: SquidPurgeClient.php:31
SquidPurgeClient\__construct
__construct( $server, $options=array())
Definition: SquidPurgeClient.php:56
SquidPurgeClient\$host
$host
Definition: SquidPurgeClient.php:31
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2584
SquidPurgeClient\log
log( $msg)
Definition: SquidPurgeClient.php:375
SquidPurgeClientPool\addClient
addClient( $client)
Definition: SquidPurgeClient.php:400