Client.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2016, ownCloud, Inc.
  5. *
  6. * @author Carlos Ferreira <carlos@reendex.com>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Daniel Kesselberg <mail@danielkesselberg.de>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Lukas Reschke <lukas@statuscode.ch>
  11. * @author Mohammed Abdellatif <m.latief@gmail.com>
  12. * @author Morris Jobke <hey@morrisjobke.de>
  13. * @author Robin Appelman <robin@icewind.nl>
  14. * @author Roeland Jago Douma <roeland@famdouma.nl>
  15. * @author Scott Shambarger <devel@shambarger.net>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OC\Http\Client;
  33. use GuzzleHttp\Client as GuzzleClient;
  34. use GuzzleHttp\Promise\PromiseInterface;
  35. use GuzzleHttp\RequestOptions;
  36. use OCP\Http\Client\IClient;
  37. use OCP\Http\Client\IPromise;
  38. use OCP\Http\Client\IResponse;
  39. use OCP\Http\Client\LocalServerException;
  40. use OCP\ICertificateManager;
  41. use OCP\IConfig;
  42. use OCP\Security\IRemoteHostValidator;
  43. use Psr\Log\LoggerInterface;
  44. use function parse_url;
  45. /**
  46. * Class Client
  47. *
  48. * @package OC\Http
  49. */
  50. class Client implements IClient {
  51. /** @var GuzzleClient */
  52. private $client;
  53. /** @var IConfig */
  54. private $config;
  55. /** @var ICertificateManager */
  56. private $certificateManager;
  57. private IRemoteHostValidator $remoteHostValidator;
  58. public function __construct(
  59. IConfig $config,
  60. ICertificateManager $certificateManager,
  61. GuzzleClient $client,
  62. IRemoteHostValidator $remoteHostValidator,
  63. protected LoggerInterface $logger,
  64. ) {
  65. $this->config = $config;
  66. $this->client = $client;
  67. $this->certificateManager = $certificateManager;
  68. $this->remoteHostValidator = $remoteHostValidator;
  69. }
  70. private function buildRequestOptions(array $options): array {
  71. $proxy = $this->getProxyUri();
  72. $defaults = [
  73. RequestOptions::VERIFY => $this->getCertBundle(),
  74. RequestOptions::TIMEOUT => 30,
  75. ];
  76. $options['nextcloud']['allow_local_address'] = $this->isLocalAddressAllowed($options);
  77. if ($options['nextcloud']['allow_local_address'] === false) {
  78. $onRedirectFunction = function (
  79. \Psr\Http\Message\RequestInterface $request,
  80. \Psr\Http\Message\ResponseInterface $response,
  81. \Psr\Http\Message\UriInterface $uri
  82. ) use ($options) {
  83. $this->preventLocalAddress($uri->__toString(), $options);
  84. };
  85. $defaults[RequestOptions::ALLOW_REDIRECTS] = [
  86. 'on_redirect' => $onRedirectFunction
  87. ];
  88. }
  89. // Only add RequestOptions::PROXY if Nextcloud is explicitly
  90. // configured to use a proxy. This is needed in order not to override
  91. // Guzzle default values.
  92. if ($proxy !== null) {
  93. $defaults[RequestOptions::PROXY] = $proxy;
  94. }
  95. $options = array_merge($defaults, $options);
  96. if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
  97. $options[RequestOptions::HEADERS]['User-Agent'] = 'Nextcloud Server Crawler';
  98. }
  99. if (!isset($options[RequestOptions::HEADERS]['Accept-Encoding'])) {
  100. $options[RequestOptions::HEADERS]['Accept-Encoding'] = 'gzip';
  101. }
  102. // Fallback for save_to
  103. if (isset($options['save_to'])) {
  104. $options['sink'] = $options['save_to'];
  105. unset($options['save_to']);
  106. }
  107. return $options;
  108. }
  109. private function getCertBundle(): string {
  110. // If the instance is not yet setup we need to use the static path as
  111. // $this->certificateManager->getAbsoluteBundlePath() tries to instantiate
  112. // a view
  113. if (!$this->config->getSystemValueBool('installed', false)) {
  114. return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
  115. }
  116. return $this->certificateManager->getAbsoluteBundlePath();
  117. }
  118. /**
  119. * Returns a null or an associative array specifying the proxy URI for
  120. * 'http' and 'https' schemes, in addition to a 'no' key value pair
  121. * providing a list of host names that should not be proxied to.
  122. *
  123. * @return array|null
  124. *
  125. * The return array looks like:
  126. * [
  127. * 'http' => 'username:password@proxy.example.com',
  128. * 'https' => 'username:password@proxy.example.com',
  129. * 'no' => ['foo.com', 'bar.com']
  130. * ]
  131. *
  132. */
  133. private function getProxyUri(): ?array {
  134. $proxyHost = $this->config->getSystemValueString('proxy', '');
  135. if ($proxyHost === '') {
  136. return null;
  137. }
  138. $proxyUserPwd = $this->config->getSystemValueString('proxyuserpwd', '');
  139. if ($proxyUserPwd !== '') {
  140. $proxyHost = $proxyUserPwd . '@' . $proxyHost;
  141. }
  142. $proxy = [
  143. 'http' => $proxyHost,
  144. 'https' => $proxyHost,
  145. ];
  146. $proxyExclude = $this->config->getSystemValue('proxyexclude', []);
  147. if ($proxyExclude !== [] && $proxyExclude !== null) {
  148. $proxy['no'] = $proxyExclude;
  149. }
  150. return $proxy;
  151. }
  152. private function isLocalAddressAllowed(array $options) : bool {
  153. if (($options['nextcloud']['allow_local_address'] ?? false) ||
  154. $this->config->getSystemValueBool('allow_local_remote_servers', false)) {
  155. return true;
  156. }
  157. return false;
  158. }
  159. protected function preventLocalAddress(string $uri, array $options): void {
  160. if ($this->isLocalAddressAllowed($options)) {
  161. return;
  162. }
  163. $host = parse_url($uri, PHP_URL_HOST);
  164. if ($host === false || $host === null) {
  165. throw new LocalServerException('Could not detect any host');
  166. }
  167. if (!$this->remoteHostValidator->isValid($host)) {
  168. throw new LocalServerException('Host "'.$host.'" violates local access rules');
  169. }
  170. }
  171. /**
  172. * Sends a GET request
  173. *
  174. * @param string $uri
  175. * @param array $options Array such as
  176. * 'query' => [
  177. * 'field' => 'abc',
  178. * 'other_field' => '123',
  179. * 'file_name' => fopen('/path/to/file', 'r'),
  180. * ],
  181. * 'headers' => [
  182. * 'foo' => 'bar',
  183. * ],
  184. * 'cookies' => [
  185. * 'foo' => 'bar',
  186. * ],
  187. * 'allow_redirects' => [
  188. * 'max' => 10, // allow at most 10 redirects.
  189. * 'strict' => true, // use "strict" RFC compliant redirects.
  190. * 'referer' => true, // add a Referer header
  191. * 'protocols' => ['https'] // only allow https URLs
  192. * ],
  193. * 'sink' => '/path/to/file', // save to a file or a stream
  194. * 'verify' => true, // bool or string to CA file
  195. * 'debug' => true,
  196. * 'timeout' => 5,
  197. * @return IResponse
  198. * @throws \Exception If the request could not get completed
  199. */
  200. public function get(string $uri, array $options = []): IResponse {
  201. $this->preventLocalAddress($uri, $options);
  202. $response = $this->client->request('get', $uri, $this->buildRequestOptions($options));
  203. $isStream = isset($options['stream']) && $options['stream'];
  204. return new Response($response, $isStream);
  205. }
  206. /**
  207. * Sends a HEAD request
  208. *
  209. * @param string $uri
  210. * @param array $options Array such as
  211. * 'headers' => [
  212. * 'foo' => 'bar',
  213. * ],
  214. * 'cookies' => [
  215. * 'foo' => 'bar',
  216. * ],
  217. * 'allow_redirects' => [
  218. * 'max' => 10, // allow at most 10 redirects.
  219. * 'strict' => true, // use "strict" RFC compliant redirects.
  220. * 'referer' => true, // add a Referer header
  221. * 'protocols' => ['https'] // only allow https URLs
  222. * ],
  223. * 'sink' => '/path/to/file', // save to a file or a stream
  224. * 'verify' => true, // bool or string to CA file
  225. * 'debug' => true,
  226. * 'timeout' => 5,
  227. * @return IResponse
  228. * @throws \Exception If the request could not get completed
  229. */
  230. public function head(string $uri, array $options = []): IResponse {
  231. $this->preventLocalAddress($uri, $options);
  232. $response = $this->client->request('head', $uri, $this->buildRequestOptions($options));
  233. return new Response($response);
  234. }
  235. /**
  236. * Sends a POST request
  237. *
  238. * @param string $uri
  239. * @param array $options Array such as
  240. * 'body' => [
  241. * 'field' => 'abc',
  242. * 'other_field' => '123',
  243. * 'file_name' => fopen('/path/to/file', 'r'),
  244. * ],
  245. * 'headers' => [
  246. * 'foo' => 'bar',
  247. * ],
  248. * 'cookies' => [
  249. * 'foo' => 'bar',
  250. * ],
  251. * 'allow_redirects' => [
  252. * 'max' => 10, // allow at most 10 redirects.
  253. * 'strict' => true, // use "strict" RFC compliant redirects.
  254. * 'referer' => true, // add a Referer header
  255. * 'protocols' => ['https'] // only allow https URLs
  256. * ],
  257. * 'sink' => '/path/to/file', // save to a file or a stream
  258. * 'verify' => true, // bool or string to CA file
  259. * 'debug' => true,
  260. * 'timeout' => 5,
  261. * @return IResponse
  262. * @throws \Exception If the request could not get completed
  263. */
  264. public function post(string $uri, array $options = []): IResponse {
  265. $this->preventLocalAddress($uri, $options);
  266. if (isset($options['body']) && is_array($options['body'])) {
  267. $options['form_params'] = $options['body'];
  268. unset($options['body']);
  269. }
  270. $response = $this->client->request('post', $uri, $this->buildRequestOptions($options));
  271. $isStream = isset($options['stream']) && $options['stream'];
  272. return new Response($response, $isStream);
  273. }
  274. /**
  275. * Sends a PUT request
  276. *
  277. * @param string $uri
  278. * @param array $options Array such as
  279. * 'body' => [
  280. * 'field' => 'abc',
  281. * 'other_field' => '123',
  282. * 'file_name' => fopen('/path/to/file', 'r'),
  283. * ],
  284. * 'headers' => [
  285. * 'foo' => 'bar',
  286. * ],
  287. * 'cookies' => [
  288. * 'foo' => 'bar',
  289. * ],
  290. * 'allow_redirects' => [
  291. * 'max' => 10, // allow at most 10 redirects.
  292. * 'strict' => true, // use "strict" RFC compliant redirects.
  293. * 'referer' => true, // add a Referer header
  294. * 'protocols' => ['https'] // only allow https URLs
  295. * ],
  296. * 'sink' => '/path/to/file', // save to a file or a stream
  297. * 'verify' => true, // bool or string to CA file
  298. * 'debug' => true,
  299. * 'timeout' => 5,
  300. * @return IResponse
  301. * @throws \Exception If the request could not get completed
  302. */
  303. public function put(string $uri, array $options = []): IResponse {
  304. $this->preventLocalAddress($uri, $options);
  305. $response = $this->client->request('put', $uri, $this->buildRequestOptions($options));
  306. return new Response($response);
  307. }
  308. /**
  309. * Sends a PATCH request
  310. *
  311. * @param string $uri
  312. * @param array $options Array such as
  313. * 'body' => [
  314. * 'field' => 'abc',
  315. * 'other_field' => '123',
  316. * 'file_name' => fopen('/path/to/file', 'r'),
  317. * ],
  318. * 'headers' => [
  319. * 'foo' => 'bar',
  320. * ],
  321. * 'cookies' => [
  322. * 'foo' => 'bar',
  323. * ],
  324. * 'allow_redirects' => [
  325. * 'max' => 10, // allow at most 10 redirects.
  326. * 'strict' => true, // use "strict" RFC compliant redirects.
  327. * 'referer' => true, // add a Referer header
  328. * 'protocols' => ['https'] // only allow https URLs
  329. * ],
  330. * 'sink' => '/path/to/file', // save to a file or a stream
  331. * 'verify' => true, // bool or string to CA file
  332. * 'debug' => true,
  333. * 'timeout' => 5,
  334. * @return IResponse
  335. * @throws \Exception If the request could not get completed
  336. */
  337. public function patch(string $uri, array $options = []): IResponse {
  338. $this->preventLocalAddress($uri, $options);
  339. $response = $this->client->request('patch', $uri, $this->buildRequestOptions($options));
  340. return new Response($response);
  341. }
  342. /**
  343. * Sends a DELETE request
  344. *
  345. * @param string $uri
  346. * @param array $options Array such as
  347. * 'body' => [
  348. * 'field' => 'abc',
  349. * 'other_field' => '123',
  350. * 'file_name' => fopen('/path/to/file', 'r'),
  351. * ],
  352. * 'headers' => [
  353. * 'foo' => 'bar',
  354. * ],
  355. * 'cookies' => [
  356. * 'foo' => 'bar',
  357. * ],
  358. * 'allow_redirects' => [
  359. * 'max' => 10, // allow at most 10 redirects.
  360. * 'strict' => true, // use "strict" RFC compliant redirects.
  361. * 'referer' => true, // add a Referer header
  362. * 'protocols' => ['https'] // only allow https URLs
  363. * ],
  364. * 'sink' => '/path/to/file', // save to a file or a stream
  365. * 'verify' => true, // bool or string to CA file
  366. * 'debug' => true,
  367. * 'timeout' => 5,
  368. * @return IResponse
  369. * @throws \Exception If the request could not get completed
  370. */
  371. public function delete(string $uri, array $options = []): IResponse {
  372. $this->preventLocalAddress($uri, $options);
  373. $response = $this->client->request('delete', $uri, $this->buildRequestOptions($options));
  374. return new Response($response);
  375. }
  376. /**
  377. * Sends an OPTIONS request
  378. *
  379. * @param string $uri
  380. * @param array $options Array such as
  381. * 'body' => [
  382. * 'field' => 'abc',
  383. * 'other_field' => '123',
  384. * 'file_name' => fopen('/path/to/file', 'r'),
  385. * ],
  386. * 'headers' => [
  387. * 'foo' => 'bar',
  388. * ],
  389. * 'cookies' => [
  390. * 'foo' => 'bar',
  391. * ],
  392. * 'allow_redirects' => [
  393. * 'max' => 10, // allow at most 10 redirects.
  394. * 'strict' => true, // use "strict" RFC compliant redirects.
  395. * 'referer' => true, // add a Referer header
  396. * 'protocols' => ['https'] // only allow https URLs
  397. * ],
  398. * 'sink' => '/path/to/file', // save to a file or a stream
  399. * 'verify' => true, // bool or string to CA file
  400. * 'debug' => true,
  401. * 'timeout' => 5,
  402. * @return IResponse
  403. * @throws \Exception If the request could not get completed
  404. */
  405. public function options(string $uri, array $options = []): IResponse {
  406. $this->preventLocalAddress($uri, $options);
  407. $response = $this->client->request('options', $uri, $this->buildRequestOptions($options));
  408. return new Response($response);
  409. }
  410. /**
  411. * Get the response of a Throwable thrown by the request methods when possible
  412. *
  413. * @param \Throwable $e
  414. * @return IResponse
  415. * @throws \Throwable When $e did not have a response
  416. * @since 29.0.0
  417. */
  418. public function getResponseFromThrowable(\Throwable $e): IResponse {
  419. if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse') && $e->hasResponse()) {
  420. return new Response($e->getResponse());
  421. }
  422. throw $e;
  423. }
  424. /**
  425. * Sends a HTTP request
  426. *
  427. * @param string $method The HTTP method to use
  428. * @param string $uri
  429. * @param array $options Array such as
  430. * 'query' => [
  431. * 'field' => 'abc',
  432. * 'other_field' => '123',
  433. * 'file_name' => fopen('/path/to/file', 'r'),
  434. * ],
  435. * 'headers' => [
  436. * 'foo' => 'bar',
  437. * ],
  438. * 'cookies' => [
  439. * 'foo' => 'bar',
  440. * ],
  441. * 'allow_redirects' => [
  442. * 'max' => 10, // allow at most 10 redirects.
  443. * 'strict' => true, // use "strict" RFC compliant redirects.
  444. * 'referer' => true, // add a Referer header
  445. * 'protocols' => ['https'] // only allow https URLs
  446. * ],
  447. * 'sink' => '/path/to/file', // save to a file or a stream
  448. * 'verify' => true, // bool or string to CA file
  449. * 'debug' => true,
  450. * 'timeout' => 5,
  451. * @return IResponse
  452. * @throws \Exception If the request could not get completed
  453. */
  454. public function request(string $method, string $uri, array $options = []): IResponse {
  455. $this->preventLocalAddress($uri, $options);
  456. $response = $this->client->request($method, $uri, $this->buildRequestOptions($options));
  457. $isStream = isset($options['stream']) && $options['stream'];
  458. return new Response($response, $isStream);
  459. }
  460. protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise {
  461. return new GuzzlePromiseAdapter(
  462. $promise,
  463. $this->logger
  464. );
  465. }
  466. /**
  467. * Sends an asynchronous GET request
  468. *
  469. * @param string $uri
  470. * @param array $options Array such as
  471. * 'query' => [
  472. * 'field' => 'abc',
  473. * 'other_field' => '123',
  474. * 'file_name' => fopen('/path/to/file', 'r'),
  475. * ],
  476. * 'headers' => [
  477. * 'foo' => 'bar',
  478. * ],
  479. * 'cookies' => [
  480. * 'foo' => 'bar',
  481. * ],
  482. * 'allow_redirects' => [
  483. * 'max' => 10, // allow at most 10 redirects.
  484. * 'strict' => true, // use "strict" RFC compliant redirects.
  485. * 'referer' => true, // add a Referer header
  486. * 'protocols' => ['https'] // only allow https URLs
  487. * ],
  488. * 'sink' => '/path/to/file', // save to a file or a stream
  489. * 'verify' => true, // bool or string to CA file
  490. * 'debug' => true,
  491. * 'timeout' => 5,
  492. * @return IPromise
  493. */
  494. public function getAsync(string $uri, array $options = []): IPromise {
  495. $this->preventLocalAddress($uri, $options);
  496. $response = $this->client->requestAsync('get', $uri, $this->buildRequestOptions($options));
  497. return $this->wrapGuzzlePromise($response);
  498. }
  499. /**
  500. * Sends an asynchronous HEAD request
  501. *
  502. * @param string $uri
  503. * @param array $options Array such as
  504. * 'headers' => [
  505. * 'foo' => 'bar',
  506. * ],
  507. * 'cookies' => [
  508. * 'foo' => 'bar',
  509. * ],
  510. * 'allow_redirects' => [
  511. * 'max' => 10, // allow at most 10 redirects.
  512. * 'strict' => true, // use "strict" RFC compliant redirects.
  513. * 'referer' => true, // add a Referer header
  514. * 'protocols' => ['https'] // only allow https URLs
  515. * ],
  516. * 'sink' => '/path/to/file', // save to a file or a stream
  517. * 'verify' => true, // bool or string to CA file
  518. * 'debug' => true,
  519. * 'timeout' => 5,
  520. * @return IPromise
  521. */
  522. public function headAsync(string $uri, array $options = []): IPromise {
  523. $this->preventLocalAddress($uri, $options);
  524. $response = $this->client->requestAsync('head', $uri, $this->buildRequestOptions($options));
  525. return $this->wrapGuzzlePromise($response);
  526. }
  527. /**
  528. * Sends an asynchronous POST request
  529. *
  530. * @param string $uri
  531. * @param array $options Array such as
  532. * 'body' => [
  533. * 'field' => 'abc',
  534. * 'other_field' => '123',
  535. * 'file_name' => fopen('/path/to/file', 'r'),
  536. * ],
  537. * 'headers' => [
  538. * 'foo' => 'bar',
  539. * ],
  540. * 'cookies' => [
  541. * 'foo' => 'bar',
  542. * ],
  543. * 'allow_redirects' => [
  544. * 'max' => 10, // allow at most 10 redirects.
  545. * 'strict' => true, // use "strict" RFC compliant redirects.
  546. * 'referer' => true, // add a Referer header
  547. * 'protocols' => ['https'] // only allow https URLs
  548. * ],
  549. * 'sink' => '/path/to/file', // save to a file or a stream
  550. * 'verify' => true, // bool or string to CA file
  551. * 'debug' => true,
  552. * 'timeout' => 5,
  553. * @return IPromise
  554. */
  555. public function postAsync(string $uri, array $options = []): IPromise {
  556. $this->preventLocalAddress($uri, $options);
  557. if (isset($options['body']) && is_array($options['body'])) {
  558. $options['form_params'] = $options['body'];
  559. unset($options['body']);
  560. }
  561. return $this->wrapGuzzlePromise($this->client->requestAsync('post', $uri, $this->buildRequestOptions($options)));
  562. }
  563. /**
  564. * Sends an asynchronous PUT request
  565. *
  566. * @param string $uri
  567. * @param array $options Array such as
  568. * 'body' => [
  569. * 'field' => 'abc',
  570. * 'other_field' => '123',
  571. * 'file_name' => fopen('/path/to/file', 'r'),
  572. * ],
  573. * 'headers' => [
  574. * 'foo' => 'bar',
  575. * ],
  576. * 'cookies' => [
  577. * 'foo' => 'bar',
  578. * ],
  579. * 'allow_redirects' => [
  580. * 'max' => 10, // allow at most 10 redirects.
  581. * 'strict' => true, // use "strict" RFC compliant redirects.
  582. * 'referer' => true, // add a Referer header
  583. * 'protocols' => ['https'] // only allow https URLs
  584. * ],
  585. * 'sink' => '/path/to/file', // save to a file or a stream
  586. * 'verify' => true, // bool or string to CA file
  587. * 'debug' => true,
  588. * 'timeout' => 5,
  589. * @return IPromise
  590. */
  591. public function putAsync(string $uri, array $options = []): IPromise {
  592. $this->preventLocalAddress($uri, $options);
  593. $response = $this->client->requestAsync('put', $uri, $this->buildRequestOptions($options));
  594. return $this->wrapGuzzlePromise($response);
  595. }
  596. /**
  597. * Sends an asynchronous DELETE request
  598. *
  599. * @param string $uri
  600. * @param array $options Array such as
  601. * 'body' => [
  602. * 'field' => 'abc',
  603. * 'other_field' => '123',
  604. * 'file_name' => fopen('/path/to/file', 'r'),
  605. * ],
  606. * 'headers' => [
  607. * 'foo' => 'bar',
  608. * ],
  609. * 'cookies' => [
  610. * 'foo' => 'bar',
  611. * ],
  612. * 'allow_redirects' => [
  613. * 'max' => 10, // allow at most 10 redirects.
  614. * 'strict' => true, // use "strict" RFC compliant redirects.
  615. * 'referer' => true, // add a Referer header
  616. * 'protocols' => ['https'] // only allow https URLs
  617. * ],
  618. * 'sink' => '/path/to/file', // save to a file or a stream
  619. * 'verify' => true, // bool or string to CA file
  620. * 'debug' => true,
  621. * 'timeout' => 5,
  622. * @return IPromise
  623. */
  624. public function deleteAsync(string $uri, array $options = []): IPromise {
  625. $this->preventLocalAddress($uri, $options);
  626. $response = $this->client->requestAsync('delete', $uri, $this->buildRequestOptions($options));
  627. return $this->wrapGuzzlePromise($response);
  628. }
  629. /**
  630. * Sends an asynchronous OPTIONS request
  631. *
  632. * @param string $uri
  633. * @param array $options Array such as
  634. * 'body' => [
  635. * 'field' => 'abc',
  636. * 'other_field' => '123',
  637. * 'file_name' => fopen('/path/to/file', 'r'),
  638. * ],
  639. * 'headers' => [
  640. * 'foo' => 'bar',
  641. * ],
  642. * 'cookies' => [
  643. * 'foo' => 'bar',
  644. * ],
  645. * 'allow_redirects' => [
  646. * 'max' => 10, // allow at most 10 redirects.
  647. * 'strict' => true, // use "strict" RFC compliant redirects.
  648. * 'referer' => true, // add a Referer header
  649. * 'protocols' => ['https'] // only allow https URLs
  650. * ],
  651. * 'sink' => '/path/to/file', // save to a file or a stream
  652. * 'verify' => true, // bool or string to CA file
  653. * 'debug' => true,
  654. * 'timeout' => 5,
  655. * @return IPromise
  656. */
  657. public function optionsAsync(string $uri, array $options = []): IPromise {
  658. $this->preventLocalAddress($uri, $options);
  659. $response = $this->client->requestAsync('options', $uri, $this->buildRequestOptions($options));
  660. return $this->wrapGuzzlePromise($response);
  661. }
  662. }