25 private $urlTuples = [];
27 private $pageTuples = [];
30 private const MAX_REBOUND_DELAY = 300;
38 public function __construct( array $targets, array $options = [] ) {
40 (
int)max( $options[
'reboundDelay'] ?? 0, 0 ),
41 self::MAX_REBOUND_DELAY
44 foreach ( $targets as $target ) {
46 $this->pageTuples[] = [ $target, $delay ];
48 $this->urlTuples[] = [ $target, $delay ];
55 Assert::parameterType( __CLASS__, $update,
'$update' );
56 '@phan-var self $update';
58 $this->urlTuples = array_merge( $this->urlTuples, $update->urlTuples );
59 $this->pageTuples = array_merge( $this->pageTuples, $update->pageTuples );
64 $reboundDelayByUrl = $this->resolveReboundDelayByUrl();
68 $immediatePurgeTimestamp = time();
71 $urlsWithReboundByDelay = [];
72 foreach ( $reboundDelayByUrl as
$url => $delay ) {
74 $urlsWithReboundByDelay[$delay][] =
$url;
79 foreach ( $urlsWithReboundByDelay as $delay => $urls ) {
82 'jobReleaseTimestamp' => $immediatePurgeTimestamp + $delay
95 public static function purge( array $urls ) {
103 $urls = array_unique( $urls );
105 wfDebugLog(
'squid', __METHOD__ .
': ' . implode(
' ', $urls ) );
108 $ts = microtime(
true );
110 $relayerGroup->getRelayer(
'cdn-url-purges' )->notifyMulti(
113 static function (
$url ) use ( $ts ) {
124 if ( $htcpRouting ) {
125 self::HTCPPurge( $urls );
130 self::naivePurge( $urls );
138 return array_keys( $this->resolveReboundDelayByUrl() );
144 private function resolveReboundDelayByUrl() {
149 $lb = $services->getLinkBatchFactory()->newLinkBatch()
150 ->setCaller( __METHOD__ );
151 foreach ( $this->pageTuples as [ $page, ] ) {
152 $lb->addObj( $page );
156 $reboundDelayByUrl = [];
159 $htmlCacheUpdater = $services->getHtmlCacheUpdater();
160 foreach ( $this->pageTuples as [ $page, $delay ] ) {
161 foreach ( $htmlCacheUpdater->getUrls( $page ) as
$url ) {
163 $reboundDelayByUrl[
$url] = max( $reboundDelayByUrl[
$url] ?? 0, $delay );
167 foreach ( $this->urlTuples as [
$url, $delay ] ) {
169 $reboundDelayByUrl[
$url] = max( $reboundDelayByUrl[
$url] ?? 0, $delay );
172 return $reboundDelayByUrl;
180 private static function HTCPPurge( array $urls ) {
187 if ( !defined(
"IPPROTO_IP" ) ) {
188 define(
"IPPROTO_IP", 0 );
189 define(
"IP_MULTICAST_LOOP", 34 );
190 define(
"IP_MULTICAST_TTL", 33 );
194 $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
196 $errstr = socket_strerror( socket_last_error() );
198 ": Error opening UDP socket: $errstr" );
204 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_LOOP, 0 );
205 if ( $htcpMulticastTTL != 1 ) {
207 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_TTL,
213 $ids = $idGenerator->newSequentialPerNodeIDs(
219 foreach ( $urls as
$url ) {
220 if ( !is_string(
$url ) ) {
221 throw new InvalidArgumentException(
'Bad purge URL' );
224 $conf = self::getRuleForURL(
$url, $htcpRouting );
227 "No HTCP rule configured for URL {$url} , skipping" );
231 if ( isset( $conf[
'host'] ) && isset( $conf[
'port'] ) ) {
235 foreach ( $conf as $subconf ) {
236 if ( !isset( $subconf[
'host'] ) || !isset( $subconf[
'port'] ) ) {
237 throw new RuntimeException(
"Invalid HTCP rule for URL $url\n" );
244 $htcpTransID = current( $ids );
247 $htcpSpecifier = pack(
'na4na*na8n',
251 $htcpDataLen = 8 + 2 + strlen( $htcpSpecifier );
252 $htcpLen = 4 + $htcpDataLen + 2;
257 $htcpPacket = pack(
'nxxnCxNxxa*n',
258 $htcpLen, $htcpDataLen, $htcpOpCLR,
259 $htcpTransID, $htcpSpecifier, 2 );
262 "Purging URL $url via HTCP" );
263 foreach ( $conf as $subconf ) {
264 socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
265 $subconf[
'host'], $subconf[
'port'] );
276 private static function naivePurge( array $urls ) {
280 foreach ( $urls as
$url ) {
283 $urlHost = strlen( $urlInfo[
'port'] ??
'' )
284 ? IPUtils::combineHostAndPort( $urlInfo[
'host'], (
int)$urlInfo[
'port'] )
291 'Connection' =>
'Keep-Alive',
292 'Proxy-Connection' =>
'Keep-Alive',
293 'User-Agent' =>
'MediaWiki/' .
MW_VERSION .
' ' . __CLASS__
296 foreach ( $cdnServers as $server ) {
297 $reqs[] = ( $baseReq + [
'proxy' => $server ] );
302 ->createMultiClient( [
'maxConnsPerHost' => 8,
'usePipelining' =>
true ] );
303 $http->runMulti( $reqs );
320 private static function expand(
$url ) {
330 private static function getRuleForURL(
$url, $rules ) {
331 foreach ( $rules as $regex => $routing ) {
332 if ( $regex ===
'' || preg_match( $regex,
$url ) ) {
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.