23use Wikimedia\Assert\Assert;
36 private const MAX_REBOUND_DELAY = 300;
44 public function __construct( array $targets, array $options = [] ) {
46 (
int)max( $options[
'reboundDelay'] ?? 0, 0 ),
47 self::MAX_REBOUND_DELAY
50 foreach ( $targets as $target ) {
52 $this->pageTuples[] = [ $target, $delay ];
54 $this->urlTuples[] = [ $target, $delay ];
61 Assert::parameterType( __CLASS__, $update,
'$update' );
62 '@phan-var self $update';
64 $this->urlTuples = array_merge( $this->urlTuples, $update->urlTuples );
65 $this->pageTuples = array_merge( $this->pageTuples, $update->pageTuples );
87 $immediatePurgeTimestamp = time();
90 $urlsWithReboundByDelay = [];
91 foreach ( $reboundDelayByUrl as $url => $delay ) {
93 $urlsWithReboundByDelay[$delay][] = $url;
98 foreach ( $urlsWithReboundByDelay as $delay => $urls ) {
101 'jobReleaseTimestamp' => $immediatePurgeTimestamp + $delay
104 JobQueueGroup::singleton()->lazyPush( $jobs );
114 public static function purge( array $urls ) {
122 $urls = array_unique( $urls );
124 wfDebugLog(
'squid', __METHOD__ .
': ' . implode(
' ', $urls ) );
127 $ts = microtime(
true );
128 $relayerGroup = MediaWikiServices::getInstance()->getEventRelayerGroup();
129 $relayerGroup->getRelayer(
'cdn-url-purges' )->notifyMulti(
132 static function ( $url ) use ( $ts ) {
164 $services = MediaWikiServices::getInstance();
168 $lb = $services->getLinkBatchFactory()->newLinkBatch();
169 foreach ( $this->pageTuples as list( $page, $delay ) ) {
170 $lb->addObj( $page );
174 $reboundDelayByUrl = [];
177 $htmlCacheUpdater = $services->getHtmlCacheUpdater();
178 foreach ( $this->pageTuples as list( $page, $delay ) ) {
179 foreach ( $htmlCacheUpdater->getUrls( $page ) as $url ) {
181 $reboundDelayByUrl[$url] = max( $reboundDelayByUrl[$url] ?? 0, $delay );
185 foreach ( $this->urlTuples as list( $url, $delay ) ) {
187 $reboundDelayByUrl[$url] = max( $reboundDelayByUrl[$url] ?? 0, $delay );
190 return $reboundDelayByUrl;
206 if ( !defined(
"IPPROTO_IP" ) ) {
207 define(
"IPPROTO_IP", 0 );
208 define(
"IP_MULTICAST_LOOP", 34 );
209 define(
"IP_MULTICAST_TTL", 33 );
213 $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
215 $errstr = socket_strerror( socket_last_error() );
217 ": Error opening UDP socket: $errstr" );
223 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_LOOP, 0 );
226 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_TTL,
231 $idGenerator = MediaWikiServices::getInstance()->getGlobalIdGenerator();
232 $ids = $idGenerator->newSequentialPerNodeIDs(
238 foreach ( $urls as $url ) {
239 if ( !is_string( $url ) ) {
246 "No HTCP rule configured for URL {$url} , skipping" );
250 if ( isset( $conf[
'host'] ) && isset( $conf[
'port'] ) ) {
254 foreach ( $conf as $subconf ) {
255 if ( !isset( $subconf[
'host'] ) || !isset( $subconf[
'port'] ) ) {
256 throw new MWException(
"Invalid HTCP rule for URL $url\n" );
263 $htcpTransID = current( $ids );
266 $htcpSpecifier = pack(
'na4na*na8n',
267 4,
'HEAD', strlen( $url ), $url,
270 $htcpDataLen = 8 + 2 + strlen( $htcpSpecifier );
271 $htcpLen = 4 + $htcpDataLen + 2;
276 $htcpPacket = pack(
'nxxnCxNxxa*n',
277 $htcpLen, $htcpDataLen, $htcpOpCLR,
278 $htcpTransID, $htcpSpecifier, 2 );
281 "Purging URL $url via HTCP" );
282 foreach ( $conf as $subconf ) {
283 socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
284 $subconf[
'host'], $subconf[
'port'] );
299 foreach ( $urls as $url ) {
302 $urlHost = strlen( $urlInfo[
'port'] ??
null )
303 ? IP::combineHostAndPort( $urlInfo[
'host'], $urlInfo[
'port'] )
310 'Connection' =>
'Keep-Alive',
311 'Proxy-Connection' =>
'Keep-Alive',
312 'User-Agent' =>
'MediaWiki/' .
MW_VERSION .
' ' . __CLASS__
316 $reqs[] = ( $baseReq + [
'proxy' => $server ] );
320 $http = MediaWikiServices::getInstance()->getHttpRequestFactory()
321 ->createMultiClient( [
'maxConnsPerHost' => 8,
'usePipelining' =>
true ] );
322 $http->runMulti( $reqs );
350 foreach ( $rules as $regex => $routing ) {
351 if ( $regex ===
'' || preg_match( $regex, $url ) ) {
$wgHTCPRouting
Routing configuration for HTCP multicast purging.
$wgCdnServers
List of proxy servers to purge on changes; default port is 80.
$wgHTCPMulticastTTL
HTCP multicast TTL.
const MW_VERSION
The running version of MediaWiki.
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified 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.
Handles purging the appropriate CDN objects given a list of URLs or Title instances.
__construct(array $targets, array $options=[])
static newFromTitles( $pages, $urls=[])
Create an update object from an array of Title objects, or a TitleArray object.
array[] $urlTuples
List of (URL, rebound purge delay) tuples.
static purge(array $urls)
Purges a list of CDN nodes defined in $wgCdnServers.
static getRuleForURL( $url, $rules)
Find the HTCP routing rule to use for a given URL.
static HTCPPurge(array $urls)
Send Hyper Text Caching Protocol (HTCP) CLR requests.
doUpdate()
Perform the actual work.
resolveReboundDelayByUrl()
array[] $pageTuples
List of (PageReference, rebound purge delay) tuples.
static expand( $url)
Expand local URLs to fully-qualified URLs using the internal protocol and host defined in $wgInternal...
merge(MergeableUpdate $update)
Merge this enqueued update with a new MergeableUpdate of the same qualified class name.
static naivePurge(array $urls)
Send HTTP PURGE requests for each of the URLs to all of the cache servers.
Job to purge a set of URLs from CDN.
Interface that deferrable updates should implement.
Interface that deferrable updates can implement to signal that updates can be combined.