MediaWiki  master
SwiftVirtualRESTService.php
Go to the documentation of this file.
1 <?php
30  protected $authCreds;
32  protected $authSessionTimestamp = 0;
34  protected $authErrorTimestamp = null;
36  protected $authCachedStatus = null;
38  protected $authCachedReason = null;
39 
47  public function __construct( array $params ) {
48  // set up defaults and merge them with the given params
49  $mparams = array_merge( [
50  'name' => 'swift'
51  ], $params );
52  parent::__construct( $mparams );
53  }
54 
58  protected function needsAuthRequest() {
59  if ( !$this->authCreds ) {
60  return true;
61  }
62  if ( $this->authErrorTimestamp !== null ) {
63  if ( ( time() - $this->authErrorTimestamp ) < 60 ) {
64  return $this->authCachedStatus; // failed last attempt; don't bother
65  } else { // actually retry this time
66  $this->authErrorTimestamp = null;
67  }
68  }
69  // Session keys expire after a while, so we renew them periodically
70  return ( ( time() - $this->authSessionTimestamp ) > $this->params['swiftAuthTTL'] );
71  }
72 
73  protected function applyAuthResponse( array $req ) {
74  $this->authSessionTimestamp = 0;
75  [ $rcode, $rdesc, $rhdrs, , ] = $req['response'];
76  if ( $rcode >= 200 && $rcode <= 299 ) { // OK
77  $this->authCreds = [
78  'auth_token' => $rhdrs['x-auth-token'],
79  'storage_url' => $rhdrs['x-storage-url']
80  ];
81  $this->authSessionTimestamp = time();
82  return true;
83  } elseif ( $rcode === 403 ) {
84  $this->authCachedStatus = 401;
85  $this->authCachedReason = 'Authorization Required';
86  $this->authErrorTimestamp = time();
87  return false;
88  } else {
89  $this->authCachedStatus = $rcode;
90  $this->authCachedReason = $rdesc;
91  $this->authErrorTimestamp = time();
92  return null;
93  }
94  }
95 
100  public function onRequests( array $reqs, Closure $idGeneratorFunc ) {
101  $result = [];
102  $firstReq = reset( $reqs );
103  if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
104  // This was an authentication request for work requests...
105  $result = $reqs; // no change
106  } else {
107  // These are actual work requests...
108  $needsAuth = $this->needsAuthRequest();
109  if ( $needsAuth === true ) {
110  // These are work requests and we don't have any token to use.
111  // Replace the work requests with an authentication request.
112  $result = [
113  $idGeneratorFunc() => [
114  'method' => 'GET',
115  'url' => $this->params['swiftAuthUrl'] . "/v1.0",
116  'headers' => [
117  'x-auth-user' => $this->params['swiftUser'],
118  'x-auth-key' => $this->params['swiftKey'] ],
119  'isAuth' => true,
120  'chain' => $reqs
121  ]
122  ];
123  } elseif ( $needsAuth !== false ) {
124  // These are work requests and authentication has previously failed.
125  // It is most efficient to just give failed pseudo responses back for
126  // the original work requests.
127  foreach ( $reqs as $key => $req ) {
128  $req['response'] = [
129  'code' => $this->authCachedStatus,
130  'reason' => $this->authCachedReason,
131  'headers' => [],
132  'body' => '',
133  'error' => ''
134  ];
135  $result[$key] = $req;
136  }
137  } else {
138  // These are work requests and we have a token already.
139  // Go through and mangle each request to include a token.
140  foreach ( $reqs as $key => $req ) {
141  // The default encoding treats the URL as a REST style path that uses
142  // forward slash as a hierarchical delimiter (and never otherwise).
143  // Subclasses can override this, and should be documented in any case.
144  $parts = array_map( 'rawurlencode', explode( '/', $req['url'] ) );
145  $req['url'] = $this->authCreds['storage_url'] . '/' . implode( '/', $parts );
146  $req['headers']['x-auth-token'] = $this->authCreds['auth_token'];
147  $result[$key] = $req;
148  // @TODO: add ETag/Content-Length and such as needed
149  }
150  }
151  }
152  return $result;
153  }
154 
155  public function onResponses( array $reqs, Closure $idGeneratorFunc ) {
156  $firstReq = reset( $reqs );
157  if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
158  $result = [];
159  // This was an authentication request for work requests...
160  if ( $this->applyAuthResponse( $firstReq ) ) {
161  // If it succeeded, we can substitute the work requests back.
162  // Call this recursively in order to munge and add headers.
163  $result = $this->onRequests( $firstReq['chain'], $idGeneratorFunc );
164  } else {
165  // If it failed, it is most efficient to just give failing
166  // pseudo-responses back for the actual work requests.
167  foreach ( $firstReq['chain'] as $key => $req ) {
168  $req['response'] = [
169  'code' => $this->authCachedStatus,
170  'reason' => $this->authCachedReason,
171  'headers' => [],
172  'body' => '',
173  'error' => ''
174  ];
175  $result[$key] = $req;
176  }
177  }
178  } else {
179  $result = $reqs; // no change
180  }
181  return $result;
182  }
183 }
Example virtual rest service for OpenStack Swift.
onResponses(array $reqs, Closure $idGeneratorFunc)
Mangle or replace virtual HTTP(S) requests which have been responded to.
int $authSessionTimestamp
UNIX timestamp.
onRequests(array $reqs, Closure $idGeneratorFunc)
Prepare virtual HTTP(S) requests (for this service) for execution.This method should mangle any of th...
int null $authErrorTimestamp
UNIX timestamp.
Virtual HTTP service instance that can be mounted on to a VirtualRESTService.
array $params
Key/value map.