54 public const EINTR = SOCKET_EINTR;
75 $parts = explode(
':', $server, 2 );
76 $this->host = $parts[0];
77 $this->port = $parts[1] ?? 80;
87 if ( $this->socket !==
null ) {
93 $this->
log(
"DNS error" );
97 $this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
98 socket_set_nonblock( $this->socket );
99 Wikimedia\suppressWarnings();
100 $ok = socket_connect( $this->socket,
$ip, $this->port );
101 Wikimedia\restoreWarnings();
103 $error = socket_last_error( $this->socket );
104 if ( $error !== self::EINPROGRESS ) {
105 $this->
log(
"connection error: " . socket_strerror( $error ) );
119 if ( $this->readState ==
'idle' ) {
134 if ( !strlen( $this->writeBuffer ) ) {
151 if ( $this->ip ===
null ) {
152 if ( IPUtils::isIPv4( $this->host ) ) {
154 } elseif ( IPUtils::isIPv6( $this->host ) ) {
155 throw new MWException(
'$wgCdnServers does not support IPv6' );
157 Wikimedia\suppressWarnings();
158 $this->ip = gethostbyname( $this->host );
159 if ( $this->ip === $this->host ) {
162 Wikimedia\restoreWarnings();
174 $this->socket =
false;
181 if ( $this->socket ) {
182 Wikimedia\suppressWarnings();
183 socket_set_block( $this->socket );
184 socket_shutdown( $this->socket );
185 socket_close( $this->socket );
186 Wikimedia\restoreWarnings();
188 $this->socket =
null;
189 $this->readBuffer =
'';
200 $url = str_replace(
"\n",
'', $url );
204 $host = $url[
'host'];
205 if ( isset( $url[
'port'] ) && strlen( $url[
'port'] ) > 0 ) {
206 $host .=
":" . $url[
'port'];
208 $path = $url[
'path'];
209 if ( isset( $url[
'query'] ) && is_string( $url[
'query'] ) ) {
212 $request[] =
"PURGE $path HTTP/1.1";
213 $request[] =
"Host: $host";
215 wfDeprecated(
'$wgSquidPurgeUseHostHeader = false',
'1.33' );
216 $request[] =
"PURGE $url HTTP/1.0";
218 $request[] =
"Connection: Keep-Alive";
219 $request[] =
"Proxy-Connection: Keep-Alive";
226 $this->requests[] = implode(
"\r\n", $request );
227 if ( $this->currentRequestIndex ===
null ) {
236 return strlen( $this->writeBuffer ) == 0 && $this->readState ==
'idle';
243 if ( !strlen( $this->writeBuffer ) ) {
253 if ( strlen( $this->writeBuffer ) <= self::BUFFER_SIZE ) {
256 $buf = substr( $this->writeBuffer, 0, self::BUFFER_SIZE );
258 Wikimedia\suppressWarnings();
259 $bytesSent = socket_send(
$socket, $buf, strlen( $buf ), $flags );
260 Wikimedia\restoreWarnings();
262 if ( $bytesSent ===
false ) {
263 $error = socket_last_error(
$socket );
264 if ( $error != self::EAGAIN && $error != self::EINTR ) {
265 $this->
log(
'write error: ' . socket_strerror( $error ) );
271 $this->writeBuffer = substr( $this->writeBuffer, $bytesSent );
284 Wikimedia\suppressWarnings();
285 $bytesRead = socket_recv(
$socket, $buf, self::BUFFER_SIZE, 0 );
286 Wikimedia\restoreWarnings();
287 if ( $bytesRead ===
false ) {
288 $error = socket_last_error(
$socket );
289 if ( $error != self::EAGAIN && $error != self::EINTR ) {
290 $this->
log(
'read error: ' . socket_strerror( $error ) );
294 } elseif ( $bytesRead === 0 ) {
300 $this->readBuffer .= $buf;
309 switch ( $this->readState ) {
314 $lines = explode(
"\r\n", $this->readBuffer, 2 );
315 if ( count(
$lines ) < 2 ) {
318 if ( $this->readState ==
'status' ) {
323 $this->readBuffer =
$lines[1];
326 if ( $this->bodyRemaining !==
null ) {
327 if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) {
328 $this->bodyRemaining -= strlen( $this->readBuffer );
329 $this->readBuffer =
'';
332 $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining );
333 $this->bodyRemaining = 0;
339 $this->readBuffer =
'';
343 throw new MWException( __METHOD__ .
': unexpected state' );
351 if ( !preg_match(
'!^HTTP/(\d+)\.(\d+) (\d{3}) (.*)$!',
$line, $m ) ) {
352 $this->
log(
'invalid status line' );
356 list( , , , $status, $reason ) = $m;
357 $status = intval( $status );
358 if ( $status !== 200 && $status !== 404 ) {
359 $this->
log(
"unexpected status code: $status $reason" );
363 $this->readState =
'header';
370 if ( preg_match(
'/^Content-Length: (\d+)$/i',
$line, $m ) ) {
371 $this->bodyRemaining = intval( $m[1] );
372 } elseif (
$line ===
'' ) {
373 $this->readState =
'body';
378 if ( $this->currentRequestIndex !==
null ) {
379 unset( $this->requests[$this->currentRequestIndex] );
381 if ( count( $this->requests ) ) {
382 $this->readState =
'status';
383 $this->currentRequestIndex = key( $this->requests );
386 $this->readState =
'idle';
387 $this->currentRequestIndex =
null;
388 $this->writeBuffer =
'';
390 $this->bodyRemaining =
null;
396 protected function log( $msg ) {
397 wfDebugLog(
'squid', __CLASS__ .
" ($this->host): $msg" );
$wgSquidPurgeUseHostHeader
Whether to use a Host header in purge requests sent to the proxy servers configured in $wgCdnServers.
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
static userAgent()
A standard user-agent we can use for external requests.
An HTTP 1.0 client built for the purposes of purging Squid and Varnish.
markDown()
Close the socket and ignore any future purge requests.
mixed $currentRequestIndex
processHeaderLine( $line)
processStatusLine( $line)
getReadSocketsForSelect()
Get read socket array for select()
getIP()
Get the host's IP address.
getSocket()
Open a socket if there isn't one open already, return it.
close()
Close the socket but allow it to be reopened for future purge requests.
getWriteSocketsForSelect()
Get write socket array for select()
resource false null $socket
The socket resource, or null for unconnected, or false for disabled due to error.
queuePurge( $url)
Queue a purge operation.
doWrites()
Perform pending writes.
if(!file_exists( $CREDITS)) $lines