'swift' ), $params ); parent::__construct( $mparams ); } /** * @return int|bool HTTP status on cached failure */ protected function needsAuthRequest() { if ( !$this->authCreds ) { return true; } if ( $this->authErrorTimestamp !== null ) { if ( ( time() - $this->authErrorTimestamp ) < 60 ) { return $this->authCachedStatus; // failed last attempt; don't bother } else { // actually retry this time $this->authErrorTimestamp = null; } } // Session keys expire after a while, so we renew them periodically return ( ( time() - $this->authSessionTimestamp ) > $this->params['swiftAuthTTL'] ); } protected function applyAuthResponse( array $req ) { $this->authSessionTimestamp = 0; list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $req['response']; if ( $rcode >= 200 && $rcode <= 299 ) { // OK $this->authCreds = array( 'auth_token' => $rhdrs['x-auth-token'], 'storage_url' => $rhdrs['x-storage-url'] ); $this->authSessionTimestamp = time(); return true; } elseif ( $rcode === 403 ) { $this->authCachedStatus = 401; $this->authCachedReason = 'Authorization Required'; $this->authErrorTimestamp = time(); return false; } else { $this->authCachedStatus = $rcode; $this->authCachedReason = $rdesc; $this->authErrorTimestamp = time(); return null; } } public function onRequests( array $reqs, Closure $idGeneratorFunc ) { $result = array(); $firstReq = reset( $reqs ); if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) { // This was an authentication request for work requests... $result = $reqs; // no change } else { // These are actual work requests... $needsAuth = $this->needsAuthRequest(); if ( $needsAuth === true ) { // These are work requests and we don't have any token to use. // Replace the work requests with an authentication request. $result = array( $idGeneratorFunc() => array( 'method' => 'GET', 'url' => $this->params['swiftAuthUrl'] . "/v1.0", 'headers' => array( 'x-auth-user' => $this->params['swiftUser'], 'x-auth-key' => $this->params['swiftKey'] ), 'isAuth' => true, 'chain' => $reqs ) ); } elseif ( $needsAuth !== false ) { // These are work requests and authentication has previously failed. // It is most efficient to just give failed pseudo responses back for // the original work requests. foreach ( $reqs as $key => $req ) { $req['response'] = array( 'code' => $this->authCachedStatus, 'reason' => $this->authCachedReason, 'headers' => array(), 'body' => '', 'error' => '' ); $result[$key] = $req; } } else { // These are work requests and we have a token already. // Go through and mangle each request to include a token. foreach ( $reqs as $key => $req ) { // The default encoding treats the URL as a REST style path that uses // forward slash as a hierarchical delimiter (and never otherwise). // Subclasses can override this, and should be documented in any case. $parts = array_map( 'rawurlencode', explode( '/', $req['url'] ) ); $req['url'] = $this->authCreds['storage_url'] . '/' . implode( '/', $parts ); $req['headers']['x-auth-token'] = $this->authCreds['auth_token']; $result[$key] = $req; // @TODO: add ETag/Content-Length and such as needed } } } return $result; } public function onResponses( array $reqs, Closure $idGeneratorFunc ) { $firstReq = reset( $reqs ); if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) { $result = array(); // This was an authentication request for work requests... if ( $this->applyAuthResponse( $firstReq ) ) { // If it succeeded, we can subsitute the work requests back. // Call this recursively in order to munge and add headers. $result = $this->onRequests( $firstReq['chain'], $idGeneratorFunc ); } else { // If it failed, it is most efficient to just give failing // pseudo-responses back for the actual work requests. foreach ( $firstReq['chain'] as $key => $req ) { $req['response'] = array( 'code' => $this->authCachedStatus, 'reason' => $this->authCachedReason, 'headers' => array(), 'body' => '', 'error' => '' ); $result[$key] = $req; } } } else { $result = $reqs; // no change } return $result; } }