MediaWiki REL1_30
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 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $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
96 public function onRequests( array $reqs, Closure $idGeneratorFunc ) {
97 $result = [];
98 $firstReq = reset( $reqs );
99 if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
100 // This was an authentication request for work requests...
101 $result = $reqs; // no change
102 } else {
103 // These are actual work requests...
104 $needsAuth = $this->needsAuthRequest();
105 if ( $needsAuth === true ) {
106 // These are work requests and we don't have any token to use.
107 // Replace the work requests with an authentication request.
108 $result = [
109 $idGeneratorFunc() => [
110 'method' => 'GET',
111 'url' => $this->params['swiftAuthUrl'] . "/v1.0",
112 'headers' => [
113 'x-auth-user' => $this->params['swiftUser'],
114 'x-auth-key' => $this->params['swiftKey'] ],
115 'isAuth' => true,
116 'chain' => $reqs
117 ]
118 ];
119 } elseif ( $needsAuth !== false ) {
120 // These are work requests and authentication has previously failed.
121 // It is most efficient to just give failed pseudo responses back for
122 // the original work requests.
123 foreach ( $reqs as $key => $req ) {
124 $req['response'] = [
125 'code' => $this->authCachedStatus,
126 'reason' => $this->authCachedReason,
127 'headers' => [],
128 'body' => '',
129 'error' => ''
130 ];
131 $result[$key] = $req;
132 }
133 } else {
134 // These are work requests and we have a token already.
135 // Go through and mangle each request to include a token.
136 foreach ( $reqs as $key => $req ) {
137 // The default encoding treats the URL as a REST style path that uses
138 // forward slash as a hierarchical delimiter (and never otherwise).
139 // Subclasses can override this, and should be documented in any case.
140 $parts = array_map( 'rawurlencode', explode( '/', $req['url'] ) );
141 $req['url'] = $this->authCreds['storage_url'] . '/' . implode( '/', $parts );
142 $req['headers']['x-auth-token'] = $this->authCreds['auth_token'];
143 $result[$key] = $req;
144 // @TODO: add ETag/Content-Length and such as needed
145 }
146 }
147 }
148 return $result;
149 }
150
151 public function onResponses( array $reqs, Closure $idGeneratorFunc ) {
152 $firstReq = reset( $reqs );
153 if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
154 $result = [];
155 // This was an authentication request for work requests...
156 if ( $this->applyAuthResponse( $firstReq ) ) {
157 // If it succeeded, we can subsitute the work requests back.
158 // Call this recursively in order to munge and add headers.
159 $result = $this->onRequests( $firstReq['chain'], $idGeneratorFunc );
160 } else {
161 // If it failed, it is most efficient to just give failing
162 // pseudo-responses back for the actual work requests.
163 foreach ( $firstReq['chain'] as $key => $req ) {
164 $req['response'] = [
165 'code' => $this->authCachedStatus,
166 'reason' => $this->authCachedReason,
167 'headers' => [],
168 'body' => '',
169 'error' => ''
170 ];
171 $result[$key] = $req;
172 }
173 }
174 } else {
175 $result = $reqs; // no change
176 }
177 return $result;
178 }
179}
Example virtual rest service for OpenStack Swift @TODO: caching support (APC/memcached)
onResponses(array $reqs, Closure $idGeneratorFunc)
Mangle or replace virtual HTTP(S) requests which have been responded to.
int $authErrorTimestamp
UNIX timestamp.
int $authSessionTimestamp
UNIX timestamp.
onRequests(array $reqs, Closure $idGeneratorFunc)
Prepare virtual HTTP(S) requests (for this service) for execution.
Virtual HTTP service instance that can be mounted on to a VirtualRESTService.
array $params
Key/value map.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
this hook is for auditing only $req
Definition hooks.txt:988
namespace being checked & $result
Definition hooks.txt:2293