MediaWiki  REL1_24
SwiftVirtualRESTService.php
Go to the documentation of this file.
00001 <?php
00028 class SwiftVirtualRESTService extends VirtualRESTService {
00030     protected $authCreds;
00032     protected $authSessionTimestamp = 0;
00034     protected $authErrorTimestamp = null;
00036     protected $authCachedStatus = null;
00038     protected $authCachedReason = null;
00039 
00047     public function __construct( array $params ) {
00048         parent::__construct( $params );
00049     }
00050 
00054     protected function needsAuthRequest() {
00055         if ( !$this->authCreds ) {
00056             return true;
00057         }
00058         if ( $this->authErrorTimestamp !== null ) {
00059             if ( ( time() - $this->authErrorTimestamp ) < 60 ) {
00060                 return $this->authCachedStatus; // failed last attempt; don't bother
00061             } else { // actually retry this time
00062                 $this->authErrorTimestamp = null;
00063             }
00064         }
00065         // Session keys expire after a while, so we renew them periodically
00066         return ( ( time() - $this->authSessionTimestamp ) > $this->params['swiftAuthTTL'] );
00067     }
00068 
00069     protected function applyAuthResponse( array $req ) {
00070         $this->authSessionTimestamp = 0;
00071         list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $req['response'];
00072         if ( $rcode >= 200 && $rcode <= 299 ) { // OK
00073             $this->authCreds = array(
00074                 'auth_token'  => $rhdrs['x-auth-token'],
00075                 'storage_url' => $rhdrs['x-storage-url']
00076             );
00077             $this->authSessionTimestamp = time();
00078             return true;
00079         } elseif ( $rcode === 403 ) {
00080             $this->authCachedStatus = 401;
00081             $this->authCachedReason = 'Authorization Required';
00082             $this->authErrorTimestamp = time();
00083             return false;
00084         } else {
00085             $this->authCachedStatus = $rcode;
00086             $this->authCachedReason = $rdesc;
00087             $this->authErrorTimestamp = time();
00088             return null;
00089         }
00090     }
00091 
00092     public function onRequests( array $reqs, Closure $idGeneratorFunc ) {
00093         $result = array();
00094         $firstReq = reset( $reqs );
00095         if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
00096             // This was an authentication request for work requests...
00097             $result = $reqs; // no change
00098         } else {
00099             // These are actual work requests...
00100             $needsAuth = $this->needsAuthRequest();
00101             if ( $needsAuth === true ) {
00102                 // These are work requests and we don't have any token to use.
00103                 // Replace the work requests with an authentication request.
00104                 $result = array(
00105                     $idGeneratorFunc() => array(
00106                         'method'  => 'GET',
00107                         'url'     => $this->params['swiftAuthUrl'] . "/v1.0",
00108                         'headers' => array(
00109                             'x-auth-user' => $this->params['swiftUser'],
00110                             'x-auth-key'  => $this->params['swiftKey'] ),
00111                         'isAuth'  => true,
00112                         'chain'   => $reqs
00113                     )
00114                 );
00115             } elseif ( $needsAuth !== false ) {
00116                 // These are work requests and authentication has previously failed.
00117                 // It is most efficient to just give failed pseudo responses back for
00118                 // the original work requests.
00119                 foreach ( $reqs as $key => $req ) {
00120                     $req['response'] = array(
00121                         'code'     => $this->authCachedStatus,
00122                         'reason'   => $this->authCachedReason,
00123                         'headers'  => array(),
00124                         'body'     => '',
00125                         'error'    => ''
00126                     );
00127                     $result[$key] = $req;
00128                 }
00129             } else {
00130                 // These are work requests and we have a token already.
00131                 // Go through and mangle each request to include a token.
00132                 foreach ( $reqs as $key => $req ) {
00133                     // The default encoding treats the URL as a REST style path that uses
00134                     // forward slash as a hierarchical delimiter (and never otherwise).
00135                     // Subclasses can override this, and should be documented in any case.
00136                     $parts = array_map( 'rawurlencode', explode( '/', $req['url'] ) );
00137                     $req['url'] = $this->authCreds['storage_url'] . '/' . implode( '/', $parts );
00138                     $req['headers']['x-auth-token'] = $this->authCreds['auth_token'];
00139                     $result[$key] = $req;
00140                     // @TODO: add ETag/Content-Length and such as needed
00141                 }
00142             }
00143         }
00144         return $result;
00145     }
00146 
00147     public function onResponses( array $reqs, Closure $idGeneratorFunc ) {
00148         $firstReq = reset( $reqs );
00149         if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
00150             $result = array();
00151             // This was an authentication request for work requests...
00152             if ( $this->applyAuthResponse( $firstReq ) ) {
00153                 // If it succeeded, we can subsitute the work requests back.
00154                 // Call this recursively in order to munge and add headers.
00155                 $result = $this->onRequests( $firstReq['chain'], $idGeneratorFunc );
00156             } else {
00157                 // If it failed, it is most efficient to just give failing
00158                 // pseudo-responses back for the actual work requests.
00159                 foreach ( $firstReq['chain'] as $key => $req ) {
00160                     $req['response'] = array(
00161                         'code'     => $this->authCachedStatus,
00162                         'reason'   => $this->authCachedReason,
00163                         'headers'  => array(),
00164                         'body'     => '',
00165                         'error'    => ''
00166                     );
00167                     $result[$key] = $req;
00168                 }
00169             }
00170         } else {
00171             $result = $reqs; // no change
00172         }
00173         return $result;
00174     }
00175 }