Client.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2016, ownCloud, Inc.
  5. *
  6. * @author Lukas Reschke <lukas@statuscode.ch>
  7. * @author Robin Appelman <robin@icewind.nl>
  8. *
  9. * @license AGPL-3.0
  10. *
  11. * This code is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License, version 3,
  13. * as published by the Free Software Foundation.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License, version 3,
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>
  22. *
  23. */
  24. namespace OC\Http\Client;
  25. use GuzzleHttp\Client as GuzzleClient;
  26. use GuzzleHttp\RequestOptions;
  27. use OCP\Http\Client\IClient;
  28. use OCP\Http\Client\IResponse;
  29. use OCP\ICertificateManager;
  30. use OCP\IConfig;
  31. /**
  32. * Class Client
  33. *
  34. * @package OC\Http
  35. */
  36. class Client implements IClient {
  37. /** @var GuzzleClient */
  38. private $client;
  39. /** @var IConfig */
  40. private $config;
  41. /** @var ICertificateManager */
  42. private $certificateManager;
  43. /**
  44. * @param IConfig $config
  45. * @param ICertificateManager $certificateManager
  46. * @param GuzzleClient $client
  47. */
  48. public function __construct(
  49. IConfig $config,
  50. ICertificateManager $certificateManager,
  51. GuzzleClient $client
  52. ) {
  53. $this->config = $config;
  54. $this->client = $client;
  55. $this->certificateManager = $certificateManager;
  56. }
  57. private function buildRequestOptions(array $options): array {
  58. $defaults = [
  59. RequestOptions::PROXY => $this->getProxyUri(),
  60. RequestOptions::VERIFY => $this->getCertBundle(),
  61. ];
  62. $options = array_merge($defaults, $options);
  63. if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
  64. $options[RequestOptions::HEADERS]['User-Agent'] = 'Nextcloud Server Crawler';
  65. }
  66. return $options;
  67. }
  68. private function getCertBundle(): string {
  69. if ($this->certificateManager->listCertificates() !== []) {
  70. return $this->certificateManager->getAbsoluteBundlePath();
  71. }
  72. // If the instance is not yet setup we need to use the static path as
  73. // $this->certificateManager->getAbsoluteBundlePath() tries to instantiiate
  74. // a view
  75. if ($this->config->getSystemValue('installed', false)) {
  76. return $this->certificateManager->getAbsoluteBundlePath(null);
  77. }
  78. return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
  79. }
  80. /**
  81. * Get the proxy URI
  82. *
  83. * @return string|null
  84. */
  85. private function getProxyUri(): ?string {
  86. $proxyHost = $this->config->getSystemValue('proxy', null);
  87. if ($proxyHost === null) {
  88. return null;
  89. }
  90. $proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', null);
  91. if ($proxyUserPwd === null) {
  92. return $proxyHost;
  93. }
  94. return $proxyUserPwd . '@' . $proxyHost;
  95. }
  96. /**
  97. * Sends a GET request
  98. *
  99. * @param string $uri
  100. * @param array $options Array such as
  101. * 'query' => [
  102. * 'field' => 'abc',
  103. * 'other_field' => '123',
  104. * 'file_name' => fopen('/path/to/file', 'r'),
  105. * ],
  106. * 'headers' => [
  107. * 'foo' => 'bar',
  108. * ],
  109. * 'cookies' => ['
  110. * 'foo' => 'bar',
  111. * ],
  112. * 'allow_redirects' => [
  113. * 'max' => 10, // allow at most 10 redirects.
  114. * 'strict' => true, // use "strict" RFC compliant redirects.
  115. * 'referer' => true, // add a Referer header
  116. * 'protocols' => ['https'] // only allow https URLs
  117. * ],
  118. * 'save_to' => '/path/to/file', // save to a file or a stream
  119. * 'verify' => true, // bool or string to CA file
  120. * 'debug' => true,
  121. * 'timeout' => 5,
  122. * @return IResponse
  123. * @throws \Exception If the request could not get completed
  124. */
  125. public function get(string $uri, array $options = []): IResponse {
  126. $response = $this->client->request('get', $uri, $this->buildRequestOptions($options));
  127. $isStream = isset($options['stream']) && $options['stream'];
  128. return new Response($response, $isStream);
  129. }
  130. /**
  131. * Sends a HEAD request
  132. *
  133. * @param string $uri
  134. * @param array $options Array such as
  135. * 'headers' => [
  136. * 'foo' => 'bar',
  137. * ],
  138. * 'cookies' => ['
  139. * 'foo' => 'bar',
  140. * ],
  141. * 'allow_redirects' => [
  142. * 'max' => 10, // allow at most 10 redirects.
  143. * 'strict' => true, // use "strict" RFC compliant redirects.
  144. * 'referer' => true, // add a Referer header
  145. * 'protocols' => ['https'] // only allow https URLs
  146. * ],
  147. * 'save_to' => '/path/to/file', // save to a file or a stream
  148. * 'verify' => true, // bool or string to CA file
  149. * 'debug' => true,
  150. * 'timeout' => 5,
  151. * @return IResponse
  152. * @throws \Exception If the request could not get completed
  153. */
  154. public function head(string $uri, array $options = []): IResponse {
  155. $response = $this->client->request('head', $uri, $this->buildRequestOptions($options));
  156. return new Response($response);
  157. }
  158. /**
  159. * Sends a POST request
  160. *
  161. * @param string $uri
  162. * @param array $options Array such as
  163. * 'body' => [
  164. * 'field' => 'abc',
  165. * 'other_field' => '123',
  166. * 'file_name' => fopen('/path/to/file', 'r'),
  167. * ],
  168. * 'headers' => [
  169. * 'foo' => 'bar',
  170. * ],
  171. * 'cookies' => ['
  172. * 'foo' => 'bar',
  173. * ],
  174. * 'allow_redirects' => [
  175. * 'max' => 10, // allow at most 10 redirects.
  176. * 'strict' => true, // use "strict" RFC compliant redirects.
  177. * 'referer' => true, // add a Referer header
  178. * 'protocols' => ['https'] // only allow https URLs
  179. * ],
  180. * 'save_to' => '/path/to/file', // save to a file or a stream
  181. * 'verify' => true, // bool or string to CA file
  182. * 'debug' => true,
  183. * 'timeout' => 5,
  184. * @return IResponse
  185. * @throws \Exception If the request could not get completed
  186. */
  187. public function post(string $uri, array $options = []): IResponse {
  188. if (isset($options['body']) && is_array($options['body'])) {
  189. $options['form_params'] = $options['body'];
  190. unset($options['body']);
  191. }
  192. $response = $this->client->request('post', $uri, $this->buildRequestOptions($options));
  193. return new Response($response);
  194. }
  195. /**
  196. * Sends a PUT request
  197. *
  198. * @param string $uri
  199. * @param array $options Array such as
  200. * 'body' => [
  201. * 'field' => 'abc',
  202. * 'other_field' => '123',
  203. * 'file_name' => fopen('/path/to/file', 'r'),
  204. * ],
  205. * 'headers' => [
  206. * 'foo' => 'bar',
  207. * ],
  208. * 'cookies' => ['
  209. * 'foo' => 'bar',
  210. * ],
  211. * 'allow_redirects' => [
  212. * 'max' => 10, // allow at most 10 redirects.
  213. * 'strict' => true, // use "strict" RFC compliant redirects.
  214. * 'referer' => true, // add a Referer header
  215. * 'protocols' => ['https'] // only allow https URLs
  216. * ],
  217. * 'save_to' => '/path/to/file', // save to a file or a stream
  218. * 'verify' => true, // bool or string to CA file
  219. * 'debug' => true,
  220. * 'timeout' => 5,
  221. * @return IResponse
  222. * @throws \Exception If the request could not get completed
  223. */
  224. public function put(string $uri, array $options = []): IResponse {
  225. $response = $this->client->request('put', $uri, $this->buildRequestOptions($options));
  226. return new Response($response);
  227. }
  228. /**
  229. * Sends a DELETE request
  230. *
  231. * @param string $uri
  232. * @param array $options Array such as
  233. * 'body' => [
  234. * 'field' => 'abc',
  235. * 'other_field' => '123',
  236. * 'file_name' => fopen('/path/to/file', 'r'),
  237. * ],
  238. * 'headers' => [
  239. * 'foo' => 'bar',
  240. * ],
  241. * 'cookies' => ['
  242. * 'foo' => 'bar',
  243. * ],
  244. * 'allow_redirects' => [
  245. * 'max' => 10, // allow at most 10 redirects.
  246. * 'strict' => true, // use "strict" RFC compliant redirects.
  247. * 'referer' => true, // add a Referer header
  248. * 'protocols' => ['https'] // only allow https URLs
  249. * ],
  250. * 'save_to' => '/path/to/file', // save to a file or a stream
  251. * 'verify' => true, // bool or string to CA file
  252. * 'debug' => true,
  253. * 'timeout' => 5,
  254. * @return IResponse
  255. * @throws \Exception If the request could not get completed
  256. */
  257. public function delete(string $uri, array $options = []): IResponse {
  258. $response = $this->client->request('delete', $uri, $this->buildRequestOptions($options));
  259. return new Response($response);
  260. }
  261. /**
  262. * Sends a options request
  263. *
  264. * @param string $uri
  265. * @param array $options Array such as
  266. * 'body' => [
  267. * 'field' => 'abc',
  268. * 'other_field' => '123',
  269. * 'file_name' => fopen('/path/to/file', 'r'),
  270. * ],
  271. * 'headers' => [
  272. * 'foo' => 'bar',
  273. * ],
  274. * 'cookies' => ['
  275. * 'foo' => 'bar',
  276. * ],
  277. * 'allow_redirects' => [
  278. * 'max' => 10, // allow at most 10 redirects.
  279. * 'strict' => true, // use "strict" RFC compliant redirects.
  280. * 'referer' => true, // add a Referer header
  281. * 'protocols' => ['https'] // only allow https URLs
  282. * ],
  283. * 'save_to' => '/path/to/file', // save to a file or a stream
  284. * 'verify' => true, // bool or string to CA file
  285. * 'debug' => true,
  286. * 'timeout' => 5,
  287. * @return IResponse
  288. * @throws \Exception If the request could not get completed
  289. */
  290. public function options(string $uri, array $options = []): IResponse {
  291. $response = $this->client->request('options', $uri, $this->buildRequestOptions($options));
  292. return new Response($response);
  293. }
  294. }