RestbaseVirtualRESTService.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. * Virtual REST service for RESTBase
  22. * @since 1.25
  23. */
  24. class RestbaseVirtualRESTService extends VirtualRESTService {
  25. /**
  26. * Example RESTBase v1 requests:
  27. * GET /local/v1/page/html/{title}{/revision}
  28. * POST /local/v1/transform/html/to/wikitext{/title}{/revision}
  29. * * body: array( 'html' => ... )
  30. * POST /local/v1/transform/wikitext/to/html{/title}{/revision}
  31. * * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
  32. *
  33. * @param array $params Key/value map
  34. * - url : RESTBase server URL
  35. * - domain : Wiki domain to use
  36. * - timeout : request timeout in seconds (optional)
  37. * - forwardCookies : cookies to forward to RESTBase/Parsoid (as a Cookie
  38. * header string) or false (optional)
  39. * Note: forwardCookies will in the future be a boolean
  40. * only, signifing request cookies should be forwarded
  41. * to the service; the current state is due to the way
  42. * VE handles this particular parameter
  43. * - HTTPProxy : HTTP proxy to use (optional)
  44. * - parsoidCompat : whether to parse URL as if they were meant for Parsoid
  45. * boolean (optional)
  46. * - fixedUrl : Do not append domain to the url. For example to use
  47. * English Wikipedia restbase, you would this to true
  48. * and url to https://en.wikipedia.org/api/rest_#version#
  49. */
  50. public function __construct( array $params ) {
  51. // set up defaults and merge them with the given params
  52. $mparams = array_merge( [
  53. 'name' => 'restbase',
  54. 'url' => 'http://localhost:7231/',
  55. 'domain' => 'localhost',
  56. 'timeout' => 100,
  57. 'forwardCookies' => false,
  58. 'HTTPProxy' => null,
  59. 'parsoidCompat' => false,
  60. 'fixedUrl' => false,
  61. ], $params );
  62. // Ensure that the url parameter has a trailing slash.
  63. if ( substr( $mparams['url'], -1 ) !== '/' ) {
  64. $mparams['url'] .= '/';
  65. }
  66. // Ensure the correct domain format: strip protocol, port,
  67. // and trailing slash if present. This lets us use
  68. // $wgCanonicalServer as a default value, which is very convenient.
  69. $mparams['domain'] = preg_replace(
  70. '/^(https?:\/\/)?([^\/:]+?)(:\d+)?\/?$/',
  71. '$2',
  72. $mparams['domain']
  73. );
  74. parent::__construct( $mparams );
  75. }
  76. public function onRequests( array $reqs, Closure $idGenFunc ) {
  77. if ( $this->params['parsoidCompat'] ) {
  78. return $this->onParsoidRequests( $reqs, $idGenFunc );
  79. }
  80. $result = [];
  81. foreach ( $reqs as $key => $req ) {
  82. if ( $this->params['fixedUrl'] ) {
  83. $version = explode( '/', $req['url'] )[1];
  84. $req['url'] =
  85. str_replace( '#version#', $version, $this->params['url'] ) .
  86. preg_replace( '#^local/v./#', '', $req['url'] );
  87. } else {
  88. // replace /local/ with the current domain
  89. $req['url'] = preg_replace( '#^local/#', $this->params['domain'] . '/', $req['url'] );
  90. // and prefix it with the service URL
  91. $req['url'] = $this->params['url'] . $req['url'];
  92. }
  93. // set the appropriate proxy, timeout and headers
  94. if ( $this->params['HTTPProxy'] ) {
  95. $req['proxy'] = $this->params['HTTPProxy'];
  96. }
  97. if ( $this->params['timeout'] != null ) {
  98. $req['reqTimeout'] = $this->params['timeout'];
  99. }
  100. if ( $this->params['forwardCookies'] ) {
  101. $req['headers']['Cookie'] = $this->params['forwardCookies'];
  102. }
  103. $result[$key] = $req;
  104. }
  105. return $result;
  106. }
  107. /**
  108. * Remaps Parsoid v3 requests to RESTBase v1 requests.
  109. * @param array $reqs
  110. * @param Closure $idGeneratorFunc
  111. * @return array
  112. * @throws Exception
  113. */
  114. public function onParsoidRequests( array $reqs, Closure $idGeneratorFunc ) {
  115. $result = [];
  116. foreach ( $reqs as $key => $req ) {
  117. $version = explode( '/', $req['url'] )[1];
  118. if ( $version === 'v3' ) {
  119. $result[$key] = $this->onParsoid3Request( $req, $idGeneratorFunc );
  120. } else {
  121. throw new Exception( "Only Parsoid v3 is supported." );
  122. }
  123. }
  124. return $result;
  125. }
  126. /**
  127. * Remap a Parsoid v3 request to a RESTBase v1 request.
  128. *
  129. * Example Parsoid v3 requests:
  130. * GET /local/v3/page/html/$title/{$revision}
  131. * * $revision is optional
  132. * POST /local/v3/transform/html/to/wikitext/{$title}{/$revision}
  133. * * body: array( 'html' => ... )
  134. * * $title and $revision are optional
  135. * POST /local/v3/transform/wikitext/to/html/{$title}{/$revision}
  136. * * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
  137. * * $title is optional
  138. * * $revision is optional
  139. * @param array $req
  140. * @param Closure $idGeneratorFunc
  141. * @return array
  142. * @throws Exception
  143. */
  144. public function onParsoid3Request( array $req, Closure $idGeneratorFunc ) {
  145. $parts = explode( '/', $req['url'] );
  146. list(
  147. $targetWiki, // 'local'
  148. $version, // 'v3'
  149. $action, // 'transform' or 'page'
  150. $format, // 'html' or 'wikitext'
  151. // $title, // optional
  152. // $revision, // optional
  153. ) = $parts;
  154. if ( $targetWiki !== 'local' ) {
  155. throw new Exception( "Only 'local' target wiki is currently supported" );
  156. } elseif ( $version !== 'v3' ) {
  157. throw new Exception( "Version mismatch: should not happen." );
  158. }
  159. // replace /local/ with the current domain, change v3 to v1,
  160. $req['url'] = preg_replace( '#^local/v3/#', $this->params['domain'] . '/v1/', $req['url'] );
  161. // and prefix it with the service URL
  162. $req['url'] = $this->params['url'] . $req['url'];
  163. // set the appropriate proxy, timeout and headers
  164. if ( $this->params['HTTPProxy'] ) {
  165. $req['proxy'] = $this->params['HTTPProxy'];
  166. }
  167. if ( $this->params['timeout'] != null ) {
  168. $req['reqTimeout'] = $this->params['timeout'];
  169. }
  170. if ( $this->params['forwardCookies'] ) {
  171. $req['headers']['Cookie'] = $this->params['forwardCookies'];
  172. }
  173. return $req;
  174. }
  175. }