]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/libs/virtualrest/RestbaseVirtualRESTService.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / libs / virtualrest / RestbaseVirtualRESTService.php
1 <?php
2 /**
3  * Virtual HTTP service client for RESTBase
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  */
20
21 /**
22  * Virtual REST service for RESTBase
23  * @since 1.25
24  */
25 class RestbaseVirtualRESTService extends VirtualRESTService {
26         /**
27          * Example RESTBase v1 requests:
28          *  GET /local/v1/page/html/{title}{/revision}
29          *  POST /local/v1/transform/html/to/wikitext{/title}{/revision}
30          *   * body: array( 'html' => ... )
31          *  POST /local/v1/transform/wikitext/to/html{/title}{/revision}
32          *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
33          *
34          * @param array $params Key/value map
35          *   - url            : RESTBase server URL
36          *   - domain         : Wiki domain to use
37          *   - timeout        : request timeout in seconds (optional)
38          *   - forwardCookies : cookies to forward to RESTBase/Parsoid (as a Cookie
39          *                       header string) or false (optional)
40          *                       Note: forwardCookies will in the future be a boolean
41          *                       only, signifing request cookies should be forwarded
42          *                       to the service; the current state is due to the way
43          *                       VE handles this particular parameter
44          *   - HTTPProxy      : HTTP proxy to use (optional)
45          *   - parsoidCompat  : whether to parse URL as if they were meant for Parsoid
46          *                       boolean (optional)
47          *   - fixedUrl       : Do not append domain to the url. For example to use
48          *                       English Wikipedia restbase, you would this to true
49          *                       and url to https://en.wikipedia.org/api/rest_#version#
50          */
51         public function __construct( array $params ) {
52                 // set up defaults and merge them with the given params
53                 $mparams = array_merge( [
54                         'name' => 'restbase',
55                         'url' => 'http://localhost:7231/',
56                         'domain' => 'localhost',
57                         'timeout' => 100,
58                         'forwardCookies' => false,
59                         'HTTPProxy' => null,
60                         'parsoidCompat' => false,
61                         'fixedUrl' => false,
62                 ], $params );
63                 // Ensure that the url parameter has a trailing slash.
64                 if ( substr( $mparams['url'], -1 ) !== '/' ) {
65                         $mparams['url'] .= '/';
66                 }
67                 // Ensure the correct domain format: strip protocol, port,
68                 // and trailing slash if present.  This lets us use
69                 // $wgCanonicalServer as a default value, which is very convenient.
70                 $mparams['domain'] = preg_replace(
71                         '/^(https?:\/\/)?([^\/:]+?)(:\d+)?\/?$/',
72                         '$2',
73                         $mparams['domain']
74                 );
75                 parent::__construct( $mparams );
76         }
77
78         public function onRequests( array $reqs, Closure $idGenFunc ) {
79                 if ( $this->params['parsoidCompat'] ) {
80                         return $this->onParsoidRequests( $reqs, $idGenFunc );
81                 }
82
83                 $result = [];
84                 foreach ( $reqs as $key => $req ) {
85                         if ( $this->params['fixedUrl'] ) {
86                                 $version = explode( '/', $req['url'] )[1];
87                                 $req['url'] =
88                                         str_replace( '#version#', $version, $this->params['url'] ) .
89                                         preg_replace( '#^local/v./#', '', $req['url'] );
90                         } else {
91                                 // replace /local/ with the current domain
92                                 $req['url'] = preg_replace( '#^local/#', $this->params['domain'] . '/', $req['url'] );
93                                 // and prefix it with the service URL
94                                 $req['url'] = $this->params['url'] . $req['url'];
95                         }
96
97                         // set the appropriate proxy, timeout and headers
98                         if ( $this->params['HTTPProxy'] ) {
99                                 $req['proxy'] = $this->params['HTTPProxy'];
100                         }
101                         if ( $this->params['timeout'] != null ) {
102                                 $req['reqTimeout'] = $this->params['timeout'];
103                         }
104                         if ( $this->params['forwardCookies'] ) {
105                                 $req['headers']['Cookie'] = $this->params['forwardCookies'];
106                         }
107                         $result[$key] = $req;
108                 }
109
110                 return $result;
111         }
112
113         /**
114          * Remaps Parsoid v1/v3 requests to RESTBase v1 requests.
115          * @param array $reqs
116          * @param Closure $idGeneratorFunc
117          * @return array
118          * @throws Exception
119          */
120         public function onParsoidRequests( array $reqs, Closure $idGeneratorFunc ) {
121                 $result = [];
122                 foreach ( $reqs as $key => $req ) {
123                         $version = explode( '/', $req['url'] )[1];
124                         if ( $version === 'v3' ) {
125                                 $result[$key] = $this->onParsoid3Request( $req, $idGeneratorFunc );
126                         } elseif ( $version === 'v1' ) {
127                                 $result[$key] = $this->onParsoid1Request( $req, $idGeneratorFunc );
128                         } else {
129                                 throw new Exception( "Only v1 and v3 are supported." );
130                         }
131                 }
132
133                 return $result;
134         }
135
136         /**
137          * Remap a Parsoid v1 request to a RESTBase v1 request.
138          *
139          * Example Parsoid v1 requests:
140          *  GET /local/v1/page/$title/html/$oldid
141          *   * $oldid is optional
142          *  POST /local/v1/transform/html/to/wikitext/$title/$oldid
143          *   * body: array( 'html' => ... )
144          *   * $title and $oldid are optional
145          *  POST /local/v1/transform/wikitext/to/html/$title
146          *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body' => true/false )
147          *   * $title is optional
148          *
149          * NOTE: the POST APIs aren't "real" Parsoid v1 APIs, they are just what
150          * Visual Editor "pretends" the V1 API is like.  (See
151          * ParsoidVirtualRESTService.)
152          * @param array $req
153          * @param Closure $idGeneratorFunc
154          * @return array
155          * @throws Exception
156          */
157         public function onParsoid1Request( array $req, Closure $idGeneratorFunc ) {
158                 $parts = explode( '/', $req['url'] );
159                 list(
160                         $targetWiki, // 'local'
161                         $version, // 'v1'
162                         $reqType // 'page' or 'transform'
163                 ) = $parts;
164                 if ( $targetWiki !== 'local' ) {
165                         throw new Exception( "Only 'local' target wiki is currently supported" );
166                 } elseif ( $version !== 'v1' ) {
167                         throw new Exception( "Version mismatch: should not happen." );
168                 } elseif ( $reqType !== 'page' && $reqType !== 'transform' ) {
169                         throw new Exception( "Request type must be either 'page' or 'transform'" );
170                 }
171                 $req['url'] = $this->params['url'] . $this->params['domain'] . '/v1/' . $reqType . '/';
172                 if ( $reqType === 'page' ) {
173                         $title = $parts[3];
174                         if ( $parts[4] !== 'html' ) {
175                                 throw new Exception( "Only 'html' output format is currently supported" );
176                         }
177                         $req['url'] .= 'html/' . $title;
178                         if ( isset( $parts[5] ) ) {
179                                 $req['url'] .= '/' . $parts[5];
180                         } elseif ( isset( $req['query']['oldid'] ) && $req['query']['oldid'] ) {
181                                 $req['url'] .= '/' . $req['query']['oldid'];
182                                 unset( $req['query']['oldid'] );
183                         }
184                 } elseif ( $reqType === 'transform' ) {
185                         // from / to transform
186                         $req['url'] .= $parts[3] . '/to/' . $parts[5];
187                         // the title
188                         if ( isset( $parts[6] ) ) {
189                                 $req['url'] .= '/' . $parts[6];
190                         }
191                         // revision id
192                         if ( isset( $parts[7] ) ) {
193                                 $req['url'] .= '/' . $parts[7];
194                         } elseif ( isset( $req['body']['oldid'] ) && $req['body']['oldid'] ) {
195                                 $req['url'] .= '/' . $req['body']['oldid'];
196                                 unset( $req['body']['oldid'] );
197                         }
198                         if ( $parts[4] !== 'to' ) {
199                                 throw new Exception( "Part index 4 is not 'to'" );
200                         }
201                         if ( $parts[3] === 'html' && $parts[5] === 'wikitext' ) {
202                                 if ( !isset( $req['body']['html'] ) ) {
203                                         throw new Exception( "You must set an 'html' body key for this request" );
204                                 }
205                         } elseif ( $parts[3] == 'wikitext' && $parts[5] == 'html' ) {
206                                 if ( !isset( $req['body']['wikitext'] ) ) {
207                                         throw new Exception( "You must set a 'wikitext' body key for this request" );
208                                 }
209                                 if ( isset( $req['body']['body'] ) ) {
210                                         $req['body']['body_only'] = $req['body']['body'];
211                                         unset( $req['body']['body'] );
212                                 }
213                         } else {
214                                 throw new Exception( "Transformation unsupported" );
215                         }
216                 }
217                 // set the appropriate proxy, timeout and headers
218                 if ( $this->params['HTTPProxy'] ) {
219                         $req['proxy'] = $this->params['HTTPProxy'];
220                 }
221                 if ( $this->params['timeout'] != null ) {
222                         $req['reqTimeout'] = $this->params['timeout'];
223                 }
224                 if ( $this->params['forwardCookies'] ) {
225                         $req['headers']['Cookie'] = $this->params['forwardCookies'];
226                 }
227
228                 return $req;
229         }
230
231         /**
232          * Remap a Parsoid v3 request to a RESTBase v1 request.
233          *
234          * Example Parsoid v3 requests:
235          *  GET /local/v3/page/html/$title/{$revision}
236          *   * $revision is optional
237          *  POST /local/v3/transform/html/to/wikitext/{$title}{/$revision}
238          *   * body: array( 'html' => ... )
239          *   * $title and $revision are optional
240          *  POST /local/v3/transform/wikitext/to/html/{$title}{/$revision}
241          *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
242          *   * $title is optional
243          *   * $revision is optional
244          * @param array $req
245          * @param Closure $idGeneratorFunc
246          * @return array
247          * @throws Exception
248          */
249         public function onParsoid3Request( array $req, Closure $idGeneratorFunc ) {
250                 $parts = explode( '/', $req['url'] );
251                 list(
252                         $targetWiki, // 'local'
253                         $version, // 'v3'
254                         $action, // 'transform' or 'page'
255                         $format, // 'html' or 'wikitext'
256                         // $title, // optional
257                         // $revision, // optional
258                 ) = $parts;
259                 if ( $targetWiki !== 'local' ) {
260                         throw new Exception( "Only 'local' target wiki is currently supported" );
261                 } elseif ( $version !== 'v3' ) {
262                         throw new Exception( "Version mismatch: should not happen." );
263                 }
264                 // replace /local/ with the current domain, change v3 to v1,
265                 $req['url'] = preg_replace( '#^local/v3/#', $this->params['domain'] . '/v1/', $req['url'] );
266                 // and prefix it with the service URL
267                 $req['url'] = $this->params['url'] . $req['url'];
268                 // set the appropriate proxy, timeout and headers
269                 if ( $this->params['HTTPProxy'] ) {
270                         $req['proxy'] = $this->params['HTTPProxy'];
271                 }
272                 if ( $this->params['timeout'] != null ) {
273                         $req['reqTimeout'] = $this->params['timeout'];
274                 }
275                 if ( $this->params['forwardCookies'] ) {
276                         $req['headers']['Cookie'] = $this->params['forwardCookies'];
277                 }
278
279                 return $req;
280         }
281
282 }