57 $parts = explode(
':', $server, 2 );
58 $this->host = $parts[0];
59 $this->
port = isset( $parts[1] ) ? $parts[1] : 80;
69 if ( $this->socket !==
null ) {
75 $this->
log(
"DNS error" );
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 );
85 $error = socket_last_error( $this->socket );
86 if (
$error !== self::EINPROGRESS ) {
87 $this->
log(
"connection error: " . socket_strerror(
$error ) );
101 if ( $this->readState ==
'idle' ) {
105 if ( $socket ===
false ) {
116 if ( !strlen( $this->writeBuffer ) ) {
120 if ( $socket ===
false ) {
131 if ( $this->ip ===
null ) {
135 throw new MWException(
'$wgSquidServers does not support IPv6' );
138 $this->ip = gethostbyname( $this->host );
139 if ( $this->ip === $this->host ) {
154 $this->socket =
false;
161 if ( $this->socket ) {
163 socket_set_block( $this->socket );
164 socket_shutdown( $this->socket );
165 socket_close( $this->socket );
168 $this->socket =
null;
169 $this->readBuffer =
'';
179 global $wgSquidPurgeUseHostHeader;
182 if ( $wgSquidPurgeUseHostHeader ) {
184 $host = $url[
'host'];
185 if ( isset( $url[
'port'] ) && strlen( $url[
'port'] ) > 0 ) {
186 $host .=
":" . $url[
'port'];
188 $path = $url[
'path'];
189 if ( isset( $url[
'query'] ) && is_string( $url[
'query'] ) ) {
192 $request[] =
"PURGE $path HTTP/1.1";
193 $request[] =
"Host: $host";
195 $request[] =
"PURGE $url HTTP/1.0";
197 $request[] =
"Connection: Keep-Alive";
198 $request[] =
"Proxy-Connection: Keep-Alive";
204 $this->requests[] = implode(
"\r\n", $request );
205 if ( $this->currentRequestIndex ===
null ) {
214 return strlen( $this->writeBuffer ) == 0 && $this->readState ==
'idle';
221 if ( !strlen( $this->writeBuffer ) ) {
229 if ( strlen( $this->writeBuffer ) <= self::BUFFER_SIZE ) {
233 $buf = substr( $this->writeBuffer, 0, self::BUFFER_SIZE );
237 $bytesSent = socket_send(
$socket, $buf, strlen( $buf ),
$flags );
240 if ( $bytesSent ===
false ) {
242 if (
$error != self::EAGAIN &&
$error != self::EINTR ) {
243 $this->
log(
'write error: ' . socket_strerror(
$error ) );
249 $this->writeBuffer = substr( $this->writeBuffer, $bytesSent );
263 $bytesRead = socket_recv(
$socket, $buf, self::BUFFER_SIZE, 0 );
265 if ( $bytesRead ===
false ) {
267 if (
$error != self::EAGAIN &&
$error != self::EINTR ) {
268 $this->
log(
'read error: ' . socket_strerror(
$error ) );
272 } elseif ( $bytesRead === 0 ) {
278 $this->readBuffer .= $buf;
287 switch ( $this->readState ) {
292 $lines = explode(
"\r\n", $this->readBuffer, 2 );
293 if ( count(
$lines ) < 2 ) {
296 if ( $this->readState ==
'status' ) {
301 $this->readBuffer =
$lines[1];
304 if ( $this->bodyRemaining !==
null ) {
305 if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) {
306 $this->bodyRemaining -= strlen( $this->readBuffer );
307 $this->readBuffer =
'';
310 $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining );
311 $this->bodyRemaining = 0;
317 $this->readBuffer =
'';
321 throw new MWException( __METHOD__ .
': unexpected state' );
330 if ( !preg_match(
'!^HTTP/(\d+)\.(\d+) (\d{3}) (.*)$!',
$line, $m ) ) {
331 $this->
log(
'invalid status line' );
335 list( , , , $status, $reason ) = $m;
336 $status = intval( $status );
337 if ( $status !== 200 && $status !== 404 ) {
338 $this->
log(
"unexpected status code: $status $reason" );
342 $this->readState =
'header';
349 if ( preg_match(
'/^Content-Length: (\d+)$/i',
$line, $m ) ) {
350 $this->bodyRemaining = intval( $m[1] );
351 } elseif (
$line ===
'' ) {
352 $this->readState =
'body';
357 if ( $this->currentRequestIndex !==
null ) {
358 unset( $this->requests[$this->currentRequestIndex] );
360 if ( count( $this->requests ) ) {
361 $this->readState =
'status';
362 $this->currentRequestIndex =
key( $this->requests );
365 $this->readState =
'idle';
366 $this->currentRequestIndex =
null;
367 $this->writeBuffer =
'';
369 $this->bodyRemaining =
null;
375 protected function log( $msg ) {
376 wfDebugLog(
'squid', __CLASS__ .
" ($this->host): $msg" );
392 if ( isset(
$options[
'timeout'] ) ) {
393 $this->timeout =
$options[
'timeout'];
402 $this->clients[] = $client;
405 public function run() {
407 $startTime = microtime(
true );
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;
418 $sockets = $client->getWriteSocketsForSelect();
419 foreach ( $sockets
as $i => $socket ) {
420 $writeSockets[
"$clientIndex/$i"] = $socket;
423 if ( !count( $readSockets ) && !count( $writeSockets ) ) {
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" );
438 if ( microtime(
true ) - $startTime > $this->timeout * 0.99 ) {
439 wfDebugLog(
'squid', __CLASS__ .
": timeout ({$this->timeout}s)\n" );
441 } elseif ( !$numReady ) {
445 foreach ( $readSockets
as $key => $socket ) {
446 list( $clientIndex, ) = explode(
'/', $key );
447 $client = $this->clients[$clientIndex];
450 foreach ( $writeSockets
as $key => $socket ) {
451 list( $clientIndex, ) = explode(
'/', $key );
452 $client = $this->clients[$clientIndex];
457 foreach ( $this->clients
as $client ) {
458 if ( !$client->isIdle() ) {
463 foreach ( $this->clients
as $client ) {