73 $parts = explode(
':', $server, 2 );
74 $this->host = $parts[0];
75 $this->port = $parts[1] ?? 80;
85 if ( $this->socket !==
null ) {
91 $this->
log(
"DNS error" );
95 $this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
96 socket_set_nonblock( $this->socket );
97 Wikimedia\suppressWarnings();
98 $ok = socket_connect( $this->socket,
$ip, $this->port );
99 Wikimedia\restoreWarnings();
101 $error = socket_last_error( $this->socket );
102 if ( $error !== self::EINPROGRESS ) {
103 $this->
log(
"connection error: " . socket_strerror( $error ) );
117 if ( $this->readState ==
'idle' ) {
132 if ( !strlen( $this->writeBuffer ) ) {
149 if ( $this->ip ===
null ) {
150 if ( IP::isIPv4( $this->host ) ) {
152 } elseif ( IP::isIPv6( $this->host ) ) {
153 throw new MWException(
'$wgCdnServers does not support IPv6' );
155 Wikimedia\suppressWarnings();
156 $this->ip = gethostbyname( $this->host );
157 if ( $this->ip === $this->host ) {
160 Wikimedia\restoreWarnings();
172 $this->socket =
false;
179 if ( $this->socket ) {
180 Wikimedia\suppressWarnings();
181 socket_set_block( $this->socket );
182 socket_shutdown( $this->socket );
183 socket_close( $this->socket );
184 Wikimedia\restoreWarnings();
186 $this->socket =
null;
187 $this->readBuffer =
'';
198 $url = str_replace(
"\n",
'', $url );
202 $host = $url[
'host'];
203 if ( isset( $url[
'port'] ) && strlen( $url[
'port'] ) > 0 ) {
204 $host .=
":" . $url[
'port'];
206 $path = $url[
'path'];
207 if ( isset( $url[
'query'] ) && is_string( $url[
'query'] ) ) {
210 $request[] =
"PURGE $path HTTP/1.1";
211 $request[] =
"Host: $host";
213 wfDeprecated(
'$wgSquidPurgeUseHostHeader = false',
'1.33' );
214 $request[] =
"PURGE $url HTTP/1.0";
216 $request[] =
"Connection: Keep-Alive";
217 $request[] =
"Proxy-Connection: Keep-Alive";
223 $this->requests[] = implode(
"\r\n", $request );
224 if ( $this->currentRequestIndex ===
null ) {
233 return strlen( $this->writeBuffer ) == 0 && $this->readState ==
'idle';
240 if ( !strlen( $this->writeBuffer ) ) {
248 if ( strlen( $this->writeBuffer ) <= self::BUFFER_SIZE ) {
252 $buf = substr( $this->writeBuffer, 0, self::BUFFER_SIZE );
255 Wikimedia\suppressWarnings();
256 $bytesSent = socket_send(
$socket, $buf, strlen( $buf ), $flags );
257 Wikimedia\restoreWarnings();
259 if ( $bytesSent ===
false ) {
260 $error = socket_last_error(
$socket );
261 if ( $error != self::EAGAIN && $error != self::EINTR ) {
262 $this->
log(
'write error: ' . socket_strerror( $error ) );
268 $this->writeBuffer = substr( $this->writeBuffer, $bytesSent );
281 Wikimedia\suppressWarnings();
282 $bytesRead = socket_recv(
$socket, $buf, self::BUFFER_SIZE, 0 );
283 Wikimedia\restoreWarnings();
284 if ( $bytesRead ===
false ) {
285 $error = socket_last_error(
$socket );
286 if ( $error != self::EAGAIN && $error != self::EINTR ) {
287 $this->
log(
'read error: ' . socket_strerror( $error ) );
291 } elseif ( $bytesRead === 0 ) {
297 $this->readBuffer .= $buf;
306 switch ( $this->readState ) {
311 $lines = explode(
"\r\n", $this->readBuffer, 2 );
312 if ( count(
$lines ) < 2 ) {
315 if ( $this->readState ==
'status' ) {
320 $this->readBuffer =
$lines[1];
323 if ( $this->bodyRemaining !==
null ) {
324 if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) {
325 $this->bodyRemaining -= strlen( $this->readBuffer );
326 $this->readBuffer =
'';
329 $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining );
330 $this->bodyRemaining = 0;
336 $this->readBuffer =
'';
340 throw new MWException( __METHOD__ .
': unexpected state' );
348 if ( !preg_match(
'!^HTTP/(\d+)\.(\d+) (\d{3}) (.*)$!',
$line, $m ) ) {
349 $this->
log(
'invalid status line' );
353 list( , , , $status, $reason ) = $m;
354 $status = intval( $status );
355 if ( $status !== 200 && $status !== 404 ) {
356 $this->
log(
"unexpected status code: $status $reason" );
360 $this->readState =
'header';
367 if ( preg_match(
'/^Content-Length: (\d+)$/i',
$line, $m ) ) {
368 $this->bodyRemaining = intval( $m[1] );
369 } elseif (
$line ===
'' ) {
370 $this->readState =
'body';
375 if ( $this->currentRequestIndex !==
null ) {
376 unset( $this->requests[$this->currentRequestIndex] );
378 if ( count( $this->requests ) ) {
379 $this->readState =
'status';
380 $this->currentRequestIndex = key( $this->requests );
383 $this->readState =
'idle';
384 $this->currentRequestIndex =
null;
385 $this->writeBuffer =
'';
387 $this->bodyRemaining =
null;
393 protected function log( $msg ) {
394 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)
Throws 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 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.