$this->swiftUser = $config['swiftUser'];
$this->swiftKey = $config['swiftKey'];
// Optional settings
- $this->authTTL = isset( $config['swiftAuthTTL'] )
- ? $config['swiftAuthTTL']
- : 15 * 60; // some sane number
- $this->swiftTempUrlKey = isset( $config['swiftTempUrlKey'] )
- ? $config['swiftTempUrlKey']
- : '';
- $this->swiftStorageUrl = isset( $config['swiftStorageUrl'] )
- ? $config['swiftStorageUrl']
- : null;
- $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
- ? $config['shardViaHashLevels']
- : '';
- $this->rgwS3AccessKey = isset( $config['rgwS3AccessKey'] )
- ? $config['rgwS3AccessKey']
- : '';
- $this->rgwS3SecretKey = isset( $config['rgwS3SecretKey'] )
- ? $config['rgwS3SecretKey']
- : '';
+ $this->authTTL = $config['swiftAuthTTL'] ?? 15 * 60; // some sane number
+ $this->swiftTempUrlKey = $config['swiftTempUrlKey'] ?? '';
+ $this->swiftStorageUrl = $config['swiftStorageUrl'] ?? null;
+ $this->shardViaHashLevels = $config['shardViaHashLevels'] ?? '';
+ $this->rgwS3AccessKey = $config['rgwS3AccessKey'] ?? '';
+ $this->rgwS3SecretKey = $config['rgwS3SecretKey'] ?? '';
// HTTP helper client
$this->http = new MultiHttpClient( [] );
// Cache container information to mask latency
$this->memCache = $config['wanCache'];
}
// Process cache for container info
- $this->containerStatCache = new ProcessCacheLRU( 300 );
+ $this->containerStatCache = new MapCacheLRU( 300 );
// Cache auth token information to avoid RTTs
if ( !empty( $config['cacheAuthInfo'] ) && isset( $config['srvCache'] ) ) {
$this->srvCache = $config['srvCache'];
} else {
$this->srvCache = new EmptyBagOStuff();
}
- $this->readUsers = isset( $config['readUsers'] )
- ? $config['readUsers']
- : [];
- $this->writeUsers = isset( $config['writeUsers'] )
- ? $config['writeUsers']
- : [];
- $this->secureReadUsers = isset( $config['secureReadUsers'] )
- ? $config['secureReadUsers']
- : [];
- $this->secureWriteUsers = isset( $config['secureWriteUsers'] )
- ? $config['secureWriteUsers']
- : [];
+ $this->readUsers = $config['readUsers'] ?? [];
+ $this->writeUsers = $config['writeUsers'] ?? [];
+ $this->secureReadUsers = $config['secureReadUsers'] ?? [];
+ $this->secureWriteUsers = $config['secureWriteUsers'] ?? [];
}
public function getFeatures() {
}
$sha1Hash = Wikimedia\base_convert( sha1( $params['content'] ), 16, 36, 31 );
- $contentType = isset( $params['headers']['content-type'] )
- ? $params['headers']['content-type']
- : $this->getContentType( $params['dst'], $params['content'], null );
+ $contentType = $params['headers']['content-type']
+ ?? $this->getContentType( $params['dst'], $params['content'], null );
$reqs = [ [
'method' => 'PUT',
return $status;
}
$sha1Hash = Wikimedia\base_convert( $sha1Hash, 16, 36, 31 );
- $contentType = isset( $params['headers']['content-type'] )
- ? $params['headers']['content-type']
- : $this->getContentType( $params['dst'], null, $params['src'] );
+ $contentType = $params['headers']['content-type']
+ ?? $this->getContentType( $params['dst'], null, $params['src'] );
$handle = fopen( $params['src'], 'rb' );
if ( $handle === false ) { // source doesn't exist?
* @param string $path Storage path to object
* @return array New headers
*/
- protected function addMissingMetadata( array $objHdrs, $path ) {
+ protected function addMissingHashMetadata( array $objHdrs, $path ) {
if ( isset( $objHdrs['x-object-meta-sha1base36'] ) ) {
return $objHdrs; // nothing to do
}
$auth = $this->getAuthentication();
$ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging
- // Blindly create tmp files and stream to them, catching any exception if the file does
- // not exist. Doing stats here is useless and will loop infinitely in addMissingMetadata().
+ // Blindly create tmp files and stream to them, catching any exception
+ // if the file does not exist. Do not waste time doing file stats here.
$reqs = []; // (path => op)
foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch
* @param array $val Stat value
*/
public function loadListingStatInternal( $path, array $val ) {
- $this->cheapCache->set( $path, 'stat', $val );
+ $this->cheapCache->setField( $path, 'stat', $val );
}
protected function doGetFileXAttributes( array $params ) {
}
protected function doGetFileSha1base36( array $params ) {
+ // Avoid using stat entries from file listings, which never include the SHA-1 hash.
+ // Also, recompute the hash if it's not part of the metadata headers for some reason.
+ $params['requireSHA1'] = true;
+
$stat = $this->getFileStat( $params );
if ( $stat ) {
- if ( !isset( $stat['sha1'] ) ) {
- // Stat entries filled by file listings don't include SHA1
- $this->clearCache( [ $params['src'] ] );
- $stat = $this->getFileStat( $params );
- }
-
return $stat['sha1'];
} else {
return false;
if ( empty( $params['allowOB'] ) ) {
// Cancel output buffering and gzipping if set
- call_user_func( $this->obResetFunc );
+ ( $this->obResetFunc )();
}
$handle = fopen( 'php://output', 'wb' );
$auth = $this->getAuthentication();
$ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging
- // Blindly create tmp files and stream to them, catching any exception if the file does
- // not exist. Doing a stat here is useless causes infinite loops in addMissingMetadata().
+ // Blindly create tmp files and stream to them, catching any exception
+ // if the file does not exist. Do not waste time doing file stats here.
$reqs = []; // (path => op)
foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch
// Set the file stat process cache in passing
$stat = $this->getStatFromHeaders( $rhdrs );
$stat['latest'] = $isLatest;
- $this->cheapCache->set( $path, 'stat', $stat );
+ $this->cheapCache->setField( $path, 'stat', $stat );
} elseif ( $rcode === 404 ) {
$tmpFiles[$path] = false;
} else {
return null;
}
- $ttl = isset( $params['ttl'] ) ? $params['ttl'] : 86400;
+ $ttl = $params['ttl'] ?? 86400;
$expires = time() + $ttl;
if ( $this->swiftTempUrlKey != '' ) {
foreach ( $reqs as $stage => &$req ) {
list( $container, $relPath ) = $req['url'];
$req['url'] = $this->storageUrl( $auth, $container, $relPath );
- $req['headers'] = isset( $req['headers'] ) ? $req['headers'] : [];
+ $req['headers'] = $req['headers'] ?? [];
$req['headers'] = $this->authTokenHeaders( $auth ) + $req['headers'];
$httpReqsByStage[$stage][$index] = $req;
}
foreach ( $httpReqs as $index => $httpReq ) {
// Run the callback for each request of this operation
$callback = $fileOpHandles[$index]->callback;
- call_user_func_array( $callback, [ $httpReq, $statuses[$index] ] );
+ $callback( $httpReq, $statuses[$index] );
// On failure, abort all remaining requests for this operation
// (e.g. abort the DELETE request if the COPY request fails for a move)
if ( !$statuses[$index]->isOK() ) {
if ( $bypassCache ) { // purge cache
$this->containerStatCache->clear( $container );
- } elseif ( !$this->containerStatCache->has( $container, 'stat' ) ) {
+ } elseif ( !$this->containerStatCache->hasField( $container, 'stat' ) ) {
$this->primeContainerCache( [ $container ] ); // check persistent cache
}
- if ( !$this->containerStatCache->has( $container, 'stat' ) ) {
+ if ( !$this->containerStatCache->hasField( $container, 'stat' ) ) {
$auth = $this->getAuthentication();
if ( !$auth ) {
return null;
if ( $bypassCache ) {
return $stat;
} else {
- $this->containerStatCache->set( $container, 'stat', $stat ); // cache it
+ $this->containerStatCache->setField( $container, 'stat', $stat ); // cache it
$this->setContainerCache( $container, $stat ); // update persistent cache
}
} elseif ( $rcode === 404 ) {
}
}
- return $this->containerStatCache->get( $container, 'stat' );
+ return $this->containerStatCache->getField( $container, 'stat' );
}
/**
protected function doPrimeContainerCache( array $containerInfo ) {
foreach ( $containerInfo as $container => $info ) {
- $this->containerStatCache->set( $container, 'stat', $info );
+ $this->containerStatCache->setField( $container, 'stat', $info );
}
}
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $reqs[$path]['response'];
if ( $rcode === 200 || $rcode === 204 ) {
// Update the object if it is missing some headers
- $rhdrs = $this->addMissingMetadata( $rhdrs, $path );
+ if ( !empty( $params['requireSHA1'] ) ) {
+ $rhdrs = $this->addMissingHashMetadata( $rhdrs, $path );
+ }
// Load the stat array from the headers
$stat = $this->getStatFromHeaders( $rhdrs );
if ( $this->isRGW ) {
'mtime' => $this->convertSwiftDate( $rhdrs['last-modified'], TS_MW ),
// Empty objects actually return no content-length header in Ceph
'size' => isset( $rhdrs['content-length'] ) ? (int)$rhdrs['content-length'] : 0,
- 'sha1' => isset( $metadata['sha1base36'] ) ? $metadata['sha1base36'] : null,
+ 'sha1' => $metadata['sha1base36'] ?? null,
// Note: manifiest ETags are not an MD5 of the file
'md5' => ctype_xdigit( $rhdrs['etag'] ) ? $rhdrs['etag'] : null,
'xattr' => [ 'metadata' => $metadata, 'headers' => $headers ]
/**
* @param array $creds From getAuthentication()
- * @param string $container
- * @param string $object
+ * @param string|null $container
+ * @param string|null $object
* @return string
*/
protected function storageUrl( array $creds, $container = null, $object = null ) {