MediaWiki
REL1_24
|
00001 <?php 00028 class SquidUpdate { 00033 protected $urlArr; 00034 00039 public function __construct( $urlArr = array(), $maxTitles = false ) { 00040 global $wgMaxSquidPurgeTitles; 00041 if ( $maxTitles === false ) { 00042 $maxTitles = $wgMaxSquidPurgeTitles; 00043 } 00044 00045 // Remove duplicate URLs from list 00046 $urlArr = array_unique( $urlArr ); 00047 if ( count( $urlArr ) > $maxTitles ) { 00048 // Truncate to desired maximum URL count 00049 $urlArr = array_slice( $urlArr, 0, $maxTitles ); 00050 } 00051 $this->urlArr = $urlArr; 00052 } 00053 00063 public static function newFromLinksTo( Title $title ) { 00064 global $wgMaxSquidPurgeTitles; 00065 wfProfileIn( __METHOD__ ); 00066 00067 # Get a list of URLs linking to this page 00068 $dbr = wfGetDB( DB_SLAVE ); 00069 $res = $dbr->select( array( 'links', 'page' ), 00070 array( 'page_namespace', 'page_title' ), 00071 array( 00072 'pl_namespace' => $title->getNamespace(), 00073 'pl_title' => $title->getDBkey(), 00074 'pl_from=page_id' ), 00075 __METHOD__ ); 00076 $blurlArr = $title->getSquidURLs(); 00077 if ( $res->numRows() <= $wgMaxSquidPurgeTitles ) { 00078 foreach ( $res as $BL ) { 00079 $tobj = Title::makeTitle( $BL->page_namespace, $BL->page_title ); 00080 $blurlArr[] = $tobj->getInternalURL(); 00081 } 00082 } 00083 00084 wfProfileOut( __METHOD__ ); 00085 00086 return new SquidUpdate( $blurlArr ); 00087 } 00088 00096 public static function newFromTitles( $titles, $urlArr = array() ) { 00097 global $wgMaxSquidPurgeTitles; 00098 $i = 0; 00100 foreach ( $titles as $title ) { 00101 $urlArr[] = $title->getInternalURL(); 00102 if ( $i++ > $wgMaxSquidPurgeTitles ) { 00103 break; 00104 } 00105 } 00106 00107 return new SquidUpdate( $urlArr ); 00108 } 00109 00114 public static function newSimplePurge( Title $title ) { 00115 $urlArr = $title->getSquidURLs(); 00116 00117 return new SquidUpdate( $urlArr ); 00118 } 00119 00123 public function doUpdate() { 00124 self::purge( $this->urlArr ); 00125 } 00126 00135 public static function purge( $urlArr ) { 00136 global $wgSquidServers, $wgHTCPRouting; 00137 00138 if ( !$urlArr ) { 00139 return; 00140 } 00141 00142 wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) ); 00143 00144 if ( $wgHTCPRouting ) { 00145 self::HTCPPurge( $urlArr ); 00146 } 00147 00148 wfProfileIn( __METHOD__ ); 00149 00150 // Remove duplicate URLs 00151 $urlArr = array_unique( $urlArr ); 00152 // Maximum number of parallel connections per squid 00153 $maxSocketsPerSquid = 8; 00154 // Number of requests to send per socket 00155 // 400 seems to be a good tradeoff, opening a socket takes a while 00156 $urlsPerSocket = 400; 00157 $socketsPerSquid = ceil( count( $urlArr ) / $urlsPerSocket ); 00158 if ( $socketsPerSquid > $maxSocketsPerSquid ) { 00159 $socketsPerSquid = $maxSocketsPerSquid; 00160 } 00161 00162 $pool = new SquidPurgeClientPool; 00163 $chunks = array_chunk( $urlArr, ceil( count( $urlArr ) / $socketsPerSquid ) ); 00164 foreach ( $wgSquidServers as $server ) { 00165 foreach ( $chunks as $chunk ) { 00166 $client = new SquidPurgeClient( $server ); 00167 foreach ( $chunk as $url ) { 00168 $client->queuePurge( $url ); 00169 } 00170 $pool->addClient( $client ); 00171 } 00172 } 00173 $pool->run(); 00174 00175 wfProfileOut( __METHOD__ ); 00176 } 00177 00184 public static function HTCPPurge( $urlArr ) { 00185 global $wgHTCPRouting, $wgHTCPMulticastTTL; 00186 wfProfileIn( __METHOD__ ); 00187 00188 // HTCP CLR operation 00189 $htcpOpCLR = 4; 00190 00191 // @todo FIXME: PHP doesn't support these socket constants (include/linux/in.h) 00192 if ( !defined( "IPPROTO_IP" ) ) { 00193 define( "IPPROTO_IP", 0 ); 00194 define( "IP_MULTICAST_LOOP", 34 ); 00195 define( "IP_MULTICAST_TTL", 33 ); 00196 } 00197 00198 // pfsockopen doesn't work because we need set_sock_opt 00199 $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); 00200 if ( !$conn ) { 00201 $errstr = socket_strerror( socket_last_error() ); 00202 wfDebugLog( 'squid', __METHOD__ . 00203 ": Error opening UDP socket: $errstr" ); 00204 wfProfileOut( __METHOD__ ); 00205 00206 return; 00207 } 00208 00209 // Set socket options 00210 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_LOOP, 0 ); 00211 if ( $wgHTCPMulticastTTL != 1 ) { 00212 // Set multicast time to live (hop count) option on socket 00213 socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_TTL, 00214 $wgHTCPMulticastTTL ); 00215 } 00216 00217 // Remove duplicate URLs from collection 00218 $urlArr = array_unique( $urlArr ); 00219 // Get sequential trx IDs for packet loss counting 00220 $ids = UIDGenerator::newSequentialPerNodeIDs( 00221 'squidhtcppurge', 32, count( $urlArr ), UIDGenerator::QUICK_VOLATILE 00222 ); 00223 00224 foreach ( $urlArr as $url ) { 00225 if ( !is_string( $url ) ) { 00226 wfProfileOut( __METHOD__ ); 00227 throw new MWException( 'Bad purge URL' ); 00228 } 00229 $url = self::expand( $url ); 00230 $conf = self::getRuleForURL( $url, $wgHTCPRouting ); 00231 if ( !$conf ) { 00232 wfDebugLog( 'squid', __METHOD__ . 00233 "No HTCP rule configured for URL {$url} , skipping" ); 00234 continue; 00235 } 00236 00237 if ( isset( $conf['host'] ) && isset( $conf['port'] ) ) { 00238 // Normalize single entries 00239 $conf = array( $conf ); 00240 } 00241 foreach ( $conf as $subconf ) { 00242 if ( !isset( $subconf['host'] ) || !isset( $subconf['port'] ) ) { 00243 wfProfileOut( __METHOD__ ); 00244 throw new MWException( "Invalid HTCP rule for URL $url\n" ); 00245 } 00246 } 00247 00248 // Construct a minimal HTCP request diagram 00249 // as per RFC 2756 00250 // Opcode 'CLR', no response desired, no auth 00251 $htcpTransID = current( $ids ); 00252 next( $ids ); 00253 00254 $htcpSpecifier = pack( 'na4na*na8n', 00255 4, 'HEAD', strlen( $url ), $url, 00256 8, 'HTTP/1.0', 0 ); 00257 00258 $htcpDataLen = 8 + 2 + strlen( $htcpSpecifier ); 00259 $htcpLen = 4 + $htcpDataLen + 2; 00260 00261 // Note! Squid gets the bit order of the first 00262 // word wrong, wrt the RFC. Apparently no other 00263 // implementation exists, so adapt to Squid 00264 $htcpPacket = pack( 'nxxnCxNxxa*n', 00265 $htcpLen, $htcpDataLen, $htcpOpCLR, 00266 $htcpTransID, $htcpSpecifier, 2 ); 00267 00268 wfDebugLog( 'squid', __METHOD__ . 00269 "Purging URL $url via HTCP" ); 00270 foreach ( $conf as $subconf ) { 00271 socket_sendto( $conn, $htcpPacket, $htcpLen, 0, 00272 $subconf['host'], $subconf['port'] ); 00273 } 00274 } 00275 wfProfileOut( __METHOD__ ); 00276 } 00277 00292 public static function expand( $url ) { 00293 return wfExpandUrl( $url, PROTO_INTERNAL ); 00294 } 00295 00302 private static function getRuleForURL( $url, $rules ) { 00303 foreach ( $rules as $regex => $routing ) { 00304 if ( $regex === '' || preg_match( $regex, $url ) ) { 00305 return $routing; 00306 } 00307 } 00308 00309 return false; 00310 } 00311 }